diff --git a/crates/pop-cli/src/commands/up/parachain.rs b/crates/pop-cli/src/commands/up/parachain.rs index a59d8284..ae9be03e 100644 --- a/crates/pop-cli/src/commands/up/parachain.rs +++ b/crates/pop-cli/src/commands/up/parachain.rs @@ -9,7 +9,7 @@ use cliclack::{ use console::{Emoji, Style, Term}; use duct::cmd; use pop_common::Status; -use pop_parachains::{Error, IndexSet, NetworkNode, Zombienet}; +use pop_parachains::{clear_dmpq, Error, IndexSet, NetworkNode, Zombienet}; use std::{path::PathBuf, time::Duration}; use tokio::time::sleep; @@ -148,6 +148,28 @@ impl ZombienetCommand { } spinner.stop(result); + + // Check for any HRMP specified channels + if zombienet.hrmp_channels() { + let spinner = cliclack::spinner(); + spinner.start("Readying channels..."); + // Allow relay node time to start + tokio::time::sleep(Duration::from_secs(10)).await; + let relay_endpoint = network.relaychain().nodes()[0].client().await?; + let para_ids: Vec<_> = + network.parachains().iter().map(|p| p.para_id()).collect(); + tokio::spawn(async move { + if let Err(e) = clear_dmpq(relay_endpoint, ¶_ids).await { + spinner.stop(""); + outro_cancel(format!("{e}"))?; + } + spinner.stop("Channels ready for initialization."); + tokio::time::sleep(Duration::from_secs(2)).await; + Term::stderr().clear_last_lines(2)?; + Ok::<(), Error>(()) + }); + } + tokio::signal::ctrl_c().await?; outro("Done")?; }, diff --git a/crates/pop-parachains/Cargo.toml b/crates/pop-parachains/Cargo.toml index f685f63a..6eee3913 100644 --- a/crates/pop-parachains/Cargo.toml +++ b/crates/pop-parachains/Cargo.toml @@ -16,6 +16,9 @@ glob.workspace = true serde_json.workspace = true strum.workspace = true strum_macros.workspace = true +# Subxt dependenices need to be at least 0.37 because of the new SignedExtension +subxt-signer = { version = "0.37", features = ["subxt", "sr25519"] } +subxt = "0.37" tar.workspace = true tempfile.workspace = true thiserror.workspace = true diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index 86192db5..5a8a56e2 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -20,7 +20,7 @@ pub use new_pallet::{create_pallet_template, TemplatePalletConfig}; pub use new_parachain::instantiate_template_dir; pub use templates::{Config, Parachain, Provider}; pub use up::Zombienet; -pub use utils::helpers::is_initial_endowment_valid; +pub use utils::helpers::{clear_dmpq, is_initial_endowment_valid}; pub use utils::pallet_helpers::resolve_pallet_path; /// Information about the Node. External export from Zombienet-SDK. pub use zombienet_sdk::NetworkNode; diff --git a/crates/pop-parachains/src/up/mod.rs b/crates/pop-parachains/src/up/mod.rs index be1e618c..3dbde95b 100644 --- a/crates/pop-parachains/src/up/mod.rs +++ b/crates/pop-parachains/src/up/mod.rs @@ -31,6 +31,8 @@ pub struct Zombienet { relay_chain: RelayChain, /// The configuration required to launch parachains. parachains: IndexMap, + /// Whether any HRMP channels are to be pre-opened. + hrmp_channels: bool, } impl Zombienet { @@ -81,7 +83,7 @@ impl Zombienet { cache, ) .await?; - Ok(Self { network_config, relay_chain, parachains }) + Ok(Self { network_config, relay_chain, parachains, hrmp_channels: false }) } /// The binaries required to launch the network. @@ -268,6 +270,11 @@ impl Zombienet { return Ok(relay::default(version, runtime_version, chain, cache).await?); } + /// Whether any HRMP channels are to be pre-opened. + pub fn hrmp_channels(&self) -> bool { + self.hrmp_channels + } + /// Launches the local network. pub async fn spawn(&mut self) -> Result, Error> { // Symlink polkadot workers @@ -296,6 +303,7 @@ impl Zombienet { let config = self.network_config.configure(&self.relay_chain, &self.parachains)?; let path = config.path().to_str().expect("temp config file should have a path").into(); let network_config = NetworkConfig::load_from_toml(path)?; + self.hrmp_channels = !network_config.hrmp_channels().is_empty(); Ok(network_config.spawn_native().await?) } } diff --git a/crates/pop-parachains/src/utils/artifacts/paseo-local.scale b/crates/pop-parachains/src/utils/artifacts/paseo-local.scale new file mode 100644 index 00000000..87143a88 Binary files /dev/null and b/crates/pop-parachains/src/utils/artifacts/paseo-local.scale differ diff --git a/crates/pop-parachains/src/utils/helpers.rs b/crates/pop-parachains/src/utils/helpers.rs index 5e735762..7b20e0cb 100644 --- a/crates/pop-parachains/src/utils/helpers.rs +++ b/crates/pop-parachains/src/utils/helpers.rs @@ -6,6 +6,7 @@ use std::{ io::{self, stdin, stdout, Write}, path::Path, }; +use subxt::{ext::sp_core, OnlineClient, PolkadotConfig}; pub(crate) fn sanitize(target: &Path) -> Result<(), Error> { if target.exists() { @@ -24,6 +25,64 @@ pub(crate) fn sanitize(target: &Path) -> Result<(), Error> { Ok(()) } +/// Clears the DMPQ state for the given chains. +/// Assumes pallet-sudo is present in the runtime. +/// +/// # Arguments +/// +/// * `client` - Client for the network which state is to be modified. +/// * `para_ids` - List of ids to build the keys that will be mutated. +pub async fn clear_dmpq( + client: OnlineClient, + para_ids: &[u32], +) -> Result<(), Box> { + use subxt_signer::sr25519::dev; + + #[subxt::subxt(runtime_metadata_path = "./src/utils/artifacts/paseo-local.scale")] + mod paseo_local {} + type RuntimeCall = paseo_local::runtime_types::paseo_runtime::RuntimeCall; + + let sudo = dev::alice(); + + // Wait for blocks to be produced. + let mut sub = client.blocks().subscribe_finalized().await.unwrap(); + for _ in 0..2 { + sub.next().await; + } + + let dmp = sp_core::twox_128("Dmp".as_bytes()); + let dmp_queues = sp_core::twox_128("DownwardMessageQueues".as_bytes()); + let dmp_queue_heads = sp_core::twox_128("DownwardMessageQueueHeads".as_bytes()); + + let mut clear_dmq_keys = Vec::>::new(); + for id in para_ids { + let id = id.to_le_bytes(); + // DMP Queue Head + let mut key = dmp.to_vec(); + key.extend(&dmp_queue_heads); + key.extend(sp_core::twox_64(&id)); + key.extend(id); + clear_dmq_keys.push(key); + // DMP Queue + let mut key = dmp.to_vec(); + key.extend(&dmp_queues); + key.extend(sp_core::twox_64(&id)); + key.extend(id); + clear_dmq_keys.push(key); + } + + // Craft calls to dispatch + let kill_storage = + RuntimeCall::System(paseo_local::system::Call::kill_storage { keys: clear_dmq_keys }); + let sudo_call = paseo_local::tx().sudo().sudo(kill_storage); + + // Dispatch and watch tx + let _sudo_call_events = + client.tx().sign_and_submit_then_watch_default(&sudo_call, &sudo).await?; + + Ok(()) +} + /// Check if the initial endowment input by the user is a valid balance. /// /// # Arguments @@ -87,6 +146,7 @@ mod tests { use super::*; use crate::generator::parachain::ChainSpec; use askama::Template; + use std::env::var; use tempfile::tempdir; #[test]