From cdb93bcba36fcc1d6776589947897fde6f86dc1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Mon, 7 Apr 2025 19:59:41 +0200 Subject: [PATCH 01/38] Create writer helpers --- Cargo.lock | 75 +++- Cargo.toml | 4 + crates/pop-cli/Cargo.toml | 5 + crates/pop-cli/src/common/mod.rs | 1 + crates/pop-cli/src/common/writer.rs | 531 ++++++++++++++++++++++++++++ crates/pop-parachains/src/lib.rs | 4 +- 6 files changed, 609 insertions(+), 11 deletions(-) create mode 100644 crates/pop-cli/src/common/writer.rs diff --git a/Cargo.lock b/Cargo.lock index 2a53070de..b0fc0ee6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3545,6 +3545,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs_rollback" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f2ac32da3859259a7f1b24c86b1a40ca6671af078df6df254f25e9438b7e9" +dependencies = [ + "rustilities", + "same-file", + "tempfile", + "thiserror 2.0.11", +] + [[package]] name = "funty" version = "2.0.0" @@ -8097,6 +8109,7 @@ dependencies = [ "env_logger 0.11.7", "frame-benchmarking-cli", "frame-try-runtime", + "fs_rollback", "git2", "mockito", "open", @@ -8105,15 +8118,19 @@ dependencies = [ "pop-contracts", "pop-parachains", "pop-telemetry", + "proc-macro2", "reqwest", + "rust_writer", "serde", "serde_json", + "similar", "sp-core 32.0.0", "sp-weights", "strum 0.26.3", "strum_macros 0.26.4", "subxt 0.38.0", "subxt-signer 0.38.0", + "syn 2.0.100", "tempfile", "tokio", "toml 0.5.11", @@ -8312,9 +8329,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", "syn 2.0.100", @@ -8687,9 +8704,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -9177,6 +9194,33 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rust_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf4bb56f2de882403080044bd15dbafbdceb5437580638da25d257de8fbd86e" +dependencies = [ + "prettyplease", + "proc-macro2", + "regex", + "rust_writer_proc", + "rustilities", + "syn 2.0.100", + "thiserror 2.0.11", +] + +[[package]] +name = "rust_writer_proc" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54abd3b6b8f7e68143a45c862405827fc453433e46c5d892d59421607bea8074" +dependencies = [ + "proc-macro2", + "quote", + "rustilities", + "syn 2.0.100", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -9219,6 +9263,17 @@ dependencies = [ "nom", ] +[[package]] +name = "rustilities" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d049a83871f271465eba32e54bc8b9342795ac9675015dd9891938e6fec7f75" +dependencies = [ + "proc-macro2", + "syn 2.0.100", + "thiserror 2.0.11", +] + [[package]] name = "rustix" version = "0.36.17" @@ -11187,9 +11242,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "simple-dns" @@ -13429,14 +13484,14 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.2", "once_cell", - "rustix 0.38.42", + "rustix 1.0.3", "windows-sys 0.59.0", ] diff --git a/Cargo.toml b/Cargo.toml index ffc3216f0..6e25fef5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,10 +28,14 @@ dirs = "5.0" duct = "0.13" env_logger = "0.11.7" flate2 = "1.0.30" +fs_rollback = "3.0.1" git2 = { version = "0.18", features = ["vendored-openssl"] } glob = "0.3.1" log = "0.4.20" mockito = "1.4.0" +proc-macro2 = "1.0.86" +rust_writer = "1.0.4" +syn = "2.0.100" tar = "0.4.40" tempfile = "3.10" thiserror = "1.0.58" diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index d00a0c97a..ade7759bb 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -16,10 +16,14 @@ path = "src/main.rs" anyhow.workspace = true duct.workspace = true env_logger.workspace = true +fs_rollback.workspace = true os_info.workspace = true +proc-macro2.workspace = true reqwest.workspace = true +rust_writer.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true +syn = { workspace = true, features = ["parsing"]} tempfile.workspace = true tokio.workspace = true toml.workspace = true @@ -67,6 +71,7 @@ mockito.workspace = true subxt.workspace = true subxt-signer.workspace = true sp-weights.workspace = true +similar = "2.7.0" [features] default = ["contract", "parachain", "telemetry"] diff --git a/crates/pop-cli/src/common/mod.rs b/crates/pop-cli/src/common/mod.rs index fc6ce28a6..34308487b 100644 --- a/crates/pop-cli/src/common/mod.rs +++ b/crates/pop-cli/src/common/mod.rs @@ -19,6 +19,7 @@ pub mod runtime; #[cfg(feature = "parachain")] pub mod try_runtime; pub mod wallet; +pub(crate) mod writer; use std::fmt::{Display, Formatter, Result}; use strum::VariantArray; diff --git a/crates/pop-cli/src/common/writer.rs b/crates/pop-cli/src/common/writer.rs new file mode 100644 index 000000000..406567c71 --- /dev/null +++ b/crates/pop-cli/src/common/writer.rs @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: GPL-3.0 + +use fs_rollback::Rollback; +use proc_macro2::{Literal, Span}; +use rust_writer::{ + ast::{ + finder::{Finder, ToFind}, + implementors::ItemToFile, + mutator::{Mutator, ToMutate}, + }, + preserver::Preserver, +}; +use std::{ + cmp, + path::{Path, PathBuf}, +}; +use syn::{ + parse_quote, File, Ident, Item, ItemMacro, ItemMod, ItemType, Macro, Meta, MetaList, + Path as syn_Path, +}; + +// The different ways available to construct a runtime +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum RuntimeUsedMacro { + // The macro #[runtime] + Runtime, + // The macro construct_runtime! + ConstructRuntime, +} + +// Not more than 256 pallets are included in a runtime +pub(crate) type PalletIndex = u8; + +// Find the highest implemented pallet index in the outer enum if using the macro +// #[runtime]. +pub(crate) fn find_highest_pallet_index(ast: &File) -> anyhow::Result { + let mut highest_index: PalletIndex = 0; + let mut found = false; + for item in &ast.items { + match item { + Item::Mod(ItemMod { ident, content, .. }) + if *ident == "runtime" && content.is_some() => + { + let (_, items) = + content.as_ref().expect("content is always Some thanks to the match guard"); + for item in items { + if let Item::Type(ItemType { attrs, .. }) = item { + if let Some(pallet_index_attribute) = attrs.iter().find(|attribute| { + if let Meta::List(MetaList { + path: syn_Path { segments, .. }, .. + }) = &attribute.meta + { + segments.iter().any(|segment| segment.ident == "pallet_index") + } else { + false + } + }) { + // As the attribute at this point is for sure + // #[runtime::pallet_index(n)], meta is a MetaList where tokens + // is a TokenStream of exactly one element: the literal n. + let mut pallet_index = 0u8; + if let Meta::List(MetaList { tokens, .. }) = + &pallet_index_attribute.meta + { + pallet_index = tokens + .clone() + .into_iter() + .next() + .expect("This iterator has one element due to the attribute shape; qed;") + .to_string() + .parse::() + .expect("The macro #[runtime::pallet_index(n)] is only valid if n is a valid number, so we can parse it to PalletIndex; qed;"); + } + // Despite the pallets will likely be ordered by call_index in the + // runtime, that's not necessarily true, so we keep the highest index in + // order to give the added pallet the next index + highest_index = cmp::max(highest_index, pallet_index); + found = true; + } + } + } + }, + _ => continue, + } + } + + if !found { + return Err(anyhow::anyhow!( + format! {"Unable to find the highest pallet index in runtime file"}, + )); + } + Ok(Literal::u8_unsuffixed(highest_index.saturating_add(1))) +} + +// Determine whether a runtime's ast uses the construct_runtime! macro or the #[runtime] macro. +pub(crate) fn find_used_runtime_macro(ast: &File) -> anyhow::Result { + for item in &ast.items { + match item { + Item::Mod(ItemMod { ident, .. }) if *ident == "runtime" => { + return Ok(RuntimeUsedMacro::Runtime); + }, + Item::Macro(ItemMacro { + mac: Macro { path: syn_Path { segments, .. }, .. }, .. + }) if segments.iter().any(|segment| segment.ident == "construct_runtime") => { + return Ok(RuntimeUsedMacro::ConstructRuntime); + }, + _ => (), + } + } + Err(anyhow::anyhow!(format!("Unable to find a runtime declaration in runtime file"))) +} + +pub(crate) fn compute_pallet_related_paths( + runtime_path: &Path, +) -> (PathBuf, PathBuf, PathBuf, PathBuf) { + let runtime_src_path = runtime_path.join("src"); + let runtime_lib_path = runtime_src_path.join("lib.rs"); + let configs_rs_path = runtime_src_path.join("configs.rs"); + let configs_folder_path = runtime_src_path.join("configs"); + let configs_mod_path = configs_folder_path.join("mod.rs"); + (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) +} + +// Creates the structure for the path to the file when the new impl block will be added and add it +// to an existing rollback. +pub(crate) fn create_new_pallet_impl_path_structure<'a>( + mut rollback: Rollback<'a>, + runtime_lib_path: &'a Path, + configs_rs_path: &'a Path, + configs_folder_path: &'a Path, + configs_mod_path: &'a Path, + pallet_config_path: &'a Path, + pallet_name: &str, +) -> anyhow::Result> { + let pallet_name_ident = Ident::new(pallet_name, Span::call_site()); + + let mod_preserver = Preserver::new("mod"); + let pub_mod_preserver = Preserver::new("pub mod"); + + let pallet_mod_implementor = ItemToFile { item: parse_quote!(mod #pallet_name_ident;) }; + + match (configs_rs_path.is_file(), configs_mod_path.is_file()) { + // The runtime is using a configs module without the mod.rs sintax + (true, false) => { + if rollback.get_noted_file(&configs_rs_path).is_none() { + rollback.note_file(&configs_rs_path)?; + } + + let roll_configs_rs_path = rollback + .get_noted_file(&configs_rs_path) + .expect("This file has been noted above; qed;"); + let mut preserved_ast = rust_writer::preserver::preserve_and_parse( + roll_configs_rs_path, + &[&mod_preserver, &pub_mod_preserver], + )?; + + let mut finder = Finder::default().to_find(&pallet_mod_implementor); + let pallet_already_declared = finder.find(&preserved_ast); + if !pallet_already_declared { + let mut mutator = Mutator::default().to_mutate(&pallet_mod_implementor); + mutator.mutate(&mut preserved_ast)?; + rust_writer::preserver::resolve_preserved(&preserved_ast, roll_configs_rs_path)?; + } + + rollback.new_file(&pallet_config_path)?; + Ok(rollback) + }, + // The runtime is using a configs module with the mod.rs syntax + (false, true) => { + if rollback.get_noted_file(&configs_mod_path).is_none() { + rollback.note_file(&configs_mod_path)?; + } + + let roll_configs_mod_path = rollback + .get_noted_file(&configs_mod_path) + .expect("This file has been noted above; qed;"); + let mut preserved_ast = rust_writer::preserver::preserve_and_parse( + roll_configs_mod_path, + &[&mod_preserver, &pub_mod_preserver], + )?; + + let mut finder = Finder::default().to_find(&pallet_mod_implementor); + let pallet_already_declared = finder.find(&preserved_ast); + if !pallet_already_declared { + let mut mutator = Mutator::default().to_mutate(&pallet_mod_implementor); + mutator.mutate(&mut preserved_ast)?; + rust_writer::preserver::resolve_preserved(&preserved_ast, roll_configs_mod_path)?; + } + + rollback.new_file(&pallet_config_path)?; + Ok(rollback) + }, + // The runtime isn't using a configs module yet, we opt for the configs.rs + // convention + (false, false) => { + let configs_mod_implementor = ItemToFile { + item: parse_quote!( + pub mod configs; + ), + }; + if rollback.get_noted_file(&runtime_lib_path).is_none() { + rollback.note_file(&runtime_lib_path)?; + } + + let roll_runtime_lib_path = rollback + .get_noted_file(&runtime_lib_path) + .expect("This file has been noted above; qed;"); + let mut preserved_ast = rust_writer::preserver::preserve_and_parse( + roll_runtime_lib_path, + &[&mod_preserver, &pub_mod_preserver], + )?; + + let mut finder = Finder::default().to_find(&configs_mod_implementor); + let configs_already_declared = finder.find(&preserved_ast); + if !configs_already_declared { + let mut mutator = Mutator::default().to_mutate(&configs_mod_implementor); + mutator.mutate(&mut preserved_ast)?; + rust_writer::preserver::resolve_preserved(&preserved_ast, roll_runtime_lib_path)?; + } + + rollback.new_file(&configs_rs_path)?; + rollback.new_dir(&configs_folder_path)?; + rollback.new_file(&pallet_config_path)?; + + let roll_configs_rs_path = rollback + .get_new_file(&configs_rs_path) + .expect("The new file has been noted above; qed"); + + // New file so we can mutate it directly. + let mut ast = rust_writer::preserver::preserve_and_parse(roll_configs_rs_path, &[])?; + let mut mutator = Mutator::default().to_mutate(&pallet_mod_implementor); + mutator.mutate(&mut ast)?; + rust_writer::preserver::resolve_preserved(&ast, roll_configs_rs_path)?; + + Ok(rollback) + }, + (true, true) => unreachable!("Both approaches not supported by the compiler; qed;"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::Result; + use pop_parachains::{Config, Parachain}; + use similar::{ChangeTag, TextDiff}; + use std::path::PathBuf; + + fn setup_template_runtime_v2_macro() -> Result { + let temp_dir = tempfile::tempdir().expect("Failed to create temp dir"); + let config = Config { + symbol: "DOT".to_string(), + decimals: 18, + initial_endowment: "1000000".to_string(), + }; + pop_parachains::instantiate_standard_template( + &Parachain::Standard, + temp_dir.path(), + config, + None, + )?; + Ok(temp_dir) + } + + fn setup_template_construct_runtime_macro() -> Result { + let temp_dir = tempfile::tempdir().expect("Failed to create temp dir"); + pop_parachains::instantiate_openzeppelin_template( + &Parachain::OpenZeppelinGeneric, + temp_dir.path(), + Some("v2.0.3".to_owned()), + )?; + Ok(temp_dir) + } + + #[test] + fn find_highest_pallet_index_works() { + let temp_dir = + setup_template_runtime_v2_macro().expect("Failed to setup template and instantiate"); + + let ast = syn::parse_file( + &std::fs::read_to_string(temp_dir.path().join("runtime").join("src").join("lib.rs")) + .expect("File should be readable; qed;"), + ) + .expect("File should be parseable; qed;"); + + let highest_index = find_highest_pallet_index(&ast) + .expect("find_highest_pallet_index is supposed to be Ok"); + + // The highest index in the template is 33 + assert_eq!(highest_index.to_string(), "34"); + } + + #[test] + fn find_highest_pallet_index_fails_if_input_doesnt_use_runtime_macro() { + let temp_dir = setup_template_construct_runtime_macro() + .expect("Failed to setup template and instantiate"); + + let ast = syn::parse_file( + &std::fs::read_to_string(temp_dir.path().join("runtime").join("src").join("lib.rs")) + .expect("File should be readable; qed;"), + ) + .expect("File should be parseable; qed;"); + + let failed_call = find_highest_pallet_index(&ast); + assert!( + matches!(failed_call, Err(msg) if msg.to_string() == "Unable to find the highest pallet index in runtime file") + ); + } + + #[test] + fn find_used_runtime_macro_with_construct_runtime_works_well() { + let temp_dir = setup_template_construct_runtime_macro() + .expect("Failed to setup template and instantiate"); + + let ast = syn::parse_file( + &std::fs::read_to_string(temp_dir.path().join("runtime").join("src").join("lib.rs")) + .expect("File should be readable; qed;"), + ) + .expect("File should be parseable; qed;"); + + let used_macro = + find_used_runtime_macro(&ast).expect("find_used_runtime_macro is supposed to be Ok"); + + assert_eq!(used_macro, RuntimeUsedMacro::ConstructRuntime); + } + + #[test] + fn find_used_runtime_macro_with_runtime_macro_works_well() { + let temp_dir = + setup_template_runtime_v2_macro().expect("Failed to setup template and instantiate"); + + let ast = syn::parse_file( + &std::fs::read_to_string(temp_dir.path().join("runtime").join("src").join("lib.rs")) + .expect("File should be readable; qed;"), + ) + .expect("File should be parseable; qed;"); + + let used_macro = + find_used_runtime_macro(&ast).expect("find_used_runtime_macro is supposed to be Ok"); + + assert_eq!(used_macro, RuntimeUsedMacro::Runtime); + } + + #[test] + fn find_used_runtime_macro_fails_if_input_isnt_runtime_file() { + let temp_dir = + setup_template_runtime_v2_macro().expect("Failed to setup template and instantiate"); + + let ast = syn::parse_file( + &std::fs::read_to_string( + temp_dir.path().join("runtime").join("src").join("configs").join("mod.rs"), + ) + .expect("File should be readable; qed;"), + ) + .expect("File should be parseable; qed;"); + + let failed_call = find_used_runtime_macro(&ast); + + assert!( + matches!(failed_call, Err(msg) if msg.to_string() == "Unable to find a runtime declaration in runtime file") + ); + } + + #[test] + fn compute_pallet_related_paths_works() { + let original_path = PathBuf::from("test"); + let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = + compute_pallet_related_paths(&original_path); + + assert_eq!(runtime_lib_path, PathBuf::from("test/src/lib.rs")); + assert_eq!(configs_rs_path, PathBuf::from("test/src/configs.rs")); + assert_eq!(configs_folder_path, PathBuf::from("test/src/configs")); + assert_eq!(configs_mod_path, PathBuf::from("test/src/configs/mod.rs")); + } + + #[test] + fn create_new_pallet_impl_path_structure_configs_mod_template() { + let temp_dir = setup_template_construct_runtime_macro() + .expect("Failed to setup template and instantiate"); + + let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = + compute_pallet_related_paths(&temp_dir.path().join("runtime")); + + let pallet_config_path = configs_folder_path.join("test.rs"); + let pallet_name = "test"; + + let mut rollback = Rollback::default(); + + assert!(!pallet_config_path.exists()); + let configs_mod_before = std::fs::read_to_string(&configs_mod_path).unwrap(); + + rollback = create_new_pallet_impl_path_structure( + rollback, + &runtime_lib_path, + &configs_rs_path, + &configs_folder_path, + &configs_mod_path, + &pallet_config_path, + pallet_name, + ) + .expect("Failed to create new pallet impl path structure"); + + rollback.commit().expect("Failed to commit changes"); + + let configs_mod_after = std::fs::read_to_string(&configs_mod_path).unwrap(); + + let configs_mod_diff = TextDiff::from_lines(&configs_mod_before, &configs_mod_after); + + let expected_inserted_lines = vec!["mod test;\n"]; + let mut inserted_lines = vec![]; + + for change in configs_mod_diff.iter_all_changes() { + match change.tag() { + ChangeTag::Delete => panic!("no deletion expected"), + ChangeTag::Insert => inserted_lines.push(change.value()), + _ => (), + } + } + + assert!(pallet_config_path.exists()); + assert_eq!(expected_inserted_lines, inserted_lines); + } + + #[test] + fn create_new_pallet_impl_path_structure_configs_file_template() { + let temp_dir = setup_template_construct_runtime_macro() + .expect("Failed to setup template and instantiate"); + + let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = + compute_pallet_related_paths(&temp_dir.path().join("runtime")); + + let pallet_config_path = configs_folder_path.join("test.rs"); + let pallet_name = "test"; + + let mut rollback = Rollback::default(); + + // Create a configs.rs file at the runtime level and delete the mod.rs file, to get a + // template where the file configs.rs exists and then is used as the configs module root + std::fs::remove_file(&configs_mod_path).unwrap(); + std::fs::File::create(&configs_rs_path).unwrap(); + + assert!(!pallet_config_path.exists()); + let configs_rs_before = std::fs::read_to_string(&configs_rs_path).unwrap(); + + rollback = create_new_pallet_impl_path_structure( + rollback, + &runtime_lib_path, + &configs_rs_path, + &configs_folder_path, + &configs_mod_path, + &pallet_config_path, + pallet_name, + ) + .expect("Failed to create new pallet impl path structure"); + + rollback.commit().expect("Failed to commit changes"); + + let configs_rs_after = std::fs::read_to_string(&configs_rs_path).unwrap(); + + let configs_rs_diff = TextDiff::from_lines(&configs_rs_before, &configs_rs_after); + + let expected_inserted_lines = vec!["mod test;\n"]; + let mut inserted_lines = vec![]; + + for change in configs_rs_diff.iter_all_changes() { + match change.tag() { + ChangeTag::Delete => panic!("no deletion expected"), + ChangeTag::Insert => inserted_lines.push(change.value()), + _ => (), + } + } + + assert!(pallet_config_path.exists()); + assert_eq!(expected_inserted_lines, inserted_lines); + } + + #[test] + fn create_new_pallet_impl_path_structure_without_configs_template() { + let temp_dir = setup_template_construct_runtime_macro() + .expect("Failed to setup template and instantiate"); + + let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = + compute_pallet_related_paths(&temp_dir.path().join("runtime")); + + let pallet_config_path = configs_folder_path.join("test.rs"); + let pallet_name = "test"; + + let mut rollback = Rollback::default(); + + // Remove configs from the template and clean the lib path (the only interesting thing here + // is that pub mod configs; is added to that file). + std::fs::remove_dir_all(&configs_folder_path).unwrap(); + std::fs::File::create(&runtime_lib_path).unwrap(); + + assert!(!pallet_config_path.exists()); + let runtime_lib_before = std::fs::read_to_string(&runtime_lib_path).unwrap(); + + rollback = create_new_pallet_impl_path_structure( + rollback, + &runtime_lib_path, + &configs_rs_path, + &configs_folder_path, + &configs_mod_path, + &pallet_config_path, + pallet_name, + ) + .expect("Failed to create new pallet impl path structure"); + + rollback.commit().expect("Failed to commit changes"); + + let runtime_lib_after = std::fs::read_to_string(&runtime_lib_path).unwrap(); + + let runtime_lib_diff = TextDiff::from_lines(&runtime_lib_before, &runtime_lib_after); + + let expected_inserted_lines = vec!["pub mod configs;\n"]; + let mut inserted_lines = vec![]; + + for change in runtime_lib_diff.iter_all_changes() { + match change.tag() { + ChangeTag::Delete => panic!("no deletion expected"), + ChangeTag::Insert => inserted_lines.push(change.value()), + _ => (), + } + } + + assert!(pallet_config_path.exists()); + assert!(configs_folder_path.is_dir()); + assert_eq!(std::fs::read_to_string(&configs_rs_path).unwrap(), "mod test;\n"); + assert_eq!(expected_inserted_lines, inserted_lines); + } +} diff --git a/crates/pop-parachains/src/lib.rs b/crates/pop-parachains/src/lib.rs index c840cf89f..283418214 100644 --- a/crates/pop-parachains/src/lib.rs +++ b/crates/pop-parachains/src/lib.rs @@ -45,7 +45,9 @@ pub use deployer_providers::{DeploymentProvider, SupportedChains}; pub use errors::Error; pub use indexmap::IndexSet; pub use new_pallet::{create_pallet_template, new_pallet_options::*, TemplatePalletConfig}; -pub use new_parachain::instantiate_template_dir; +pub use new_parachain::{ + instantiate_openzeppelin_template, instantiate_standard_template, instantiate_template_dir, +}; pub use relay::{clear_dmpq, RelayChain, Reserved}; pub use try_runtime::{ binary::*, parse, parse_try_state_string, run_try_runtime, shared_parameters::*, state, From 97c3135dbc1ede883a3731422233fa202a098108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Mon, 7 Apr 2025 20:21:33 +0200 Subject: [PATCH 02/38] Bring pop add pallet to the branch --- Cargo.toml | 1 + crates/pop-cli/Cargo.toml | 1 + crates/pop-cli/src/commands/add/mod.rs | 20 ++ crates/pop-cli/src/commands/add/pallet.rs | 277 ++++++++++++++++++ .../src/commands/add/pallet/common_pallets.rs | 89 ++++++ crates/pop-cli/src/commands/mod.rs | 9 + 6 files changed, 397 insertions(+) create mode 100644 crates/pop-cli/src/commands/add/mod.rs create mode 100644 crates/pop-cli/src/commands/add/pallet.rs create mode 100644 crates/pop-cli/src/commands/add/pallet/common_pallets.rs diff --git a/Cargo.toml b/Cargo.toml index c78b8e7d8..33992c330 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ glob = "0.3.1" log = "0.4.20" mockito = "1.4.0" proc-macro2 = "1.0.86" +rustilities = "2.2.0" rust_writer = "1.0.4" syn = "2.0.100" tar = "0.4.40" diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index 050af0ce7..878e5f975 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -20,6 +20,7 @@ fs_rollback.workspace = true os_info.workspace = true proc-macro2.workspace = true reqwest.workspace = true +rustilities = { workspace = true, features = ["fmt", "manifest"]} rust_writer.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true diff --git a/crates/pop-cli/src/commands/add/mod.rs b/crates/pop-cli/src/commands/add/mod.rs new file mode 100644 index 000000000..8f9088e26 --- /dev/null +++ b/crates/pop-cli/src/commands/add/mod.rs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-3.0 + +use clap::{Args, Subcommand}; + +pub mod pallet; + +/// Arguments for adding a new feature to existing code +#[derive(Args)] +#[command(args_conflicts_with_subcommands = true)] +pub struct AddArgs { + #[command(subcommand)] + pub command: Command, +} + +#[derive(Subcommand)] +pub enum Command { + /// Add a new pallet to an existing runtime + #[clap(alias = "P")] + Pallet(pallet::AddPalletCommand), +} diff --git a/crates/pop-cli/src/commands/add/pallet.rs b/crates/pop-cli/src/commands/add/pallet.rs new file mode 100644 index 000000000..1646e23a4 --- /dev/null +++ b/crates/pop-cli/src/commands/add/pallet.rs @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::{ + cli::{traits::Cli as _, Cli}, + multiselect_pick, +}; +use clap::{error::ErrorKind, Args, Command}; +use cliclack::multiselect; +use fs_rollback::Rollback; +use pop_common::{ + manifest, + rust_writer_helpers::{self, RuntimeUsedMacro}, +}; +use rust_writer::{ + ast::{ + finder, + finder::{Finder, ToFind}, + implementors::{ItemToFile, ItemToMod, TokenStreamToMacro}, + mutator, + mutator::{Mutator, ToMutate}, + }, + preserver::Preserver, +}; +use rustilities::manifest::{ManifestDependencyConfig, ManifestDependencyOrigin}; +use std::{env, path::PathBuf}; +use strum::{EnumMessage, IntoEnumIterator}; +use syn::{parse_quote, visit_mut::VisitMut}; + +mod common_pallets; + +#[mutator(ItemToFile, ItemToFile)] +#[finder(ItemToFile, ItemToFile)] +#[impl_from] +struct PalletImplBlockImplementor; + +#[derive(Args, Debug, Clone)] +pub struct AddPalletCommand { + #[arg(short, long, value_enum, num_args(1..), required = true, help = "The pallets you want to include to your runtime.")] + pub(crate) pallets: Vec, + #[arg(short, long, help = "Specify the path to the runtime crate.")] + pub(crate) runtime_path: Option, + #[arg( + long, + help = "Pop-Cli will place the impl blocks for your pallets' Config traits inside a dedicated file under configs directory. Use this argument to point to other path." + )] + pub(crate) pallet_impl_path: Option, +} + +impl AddPalletCommand { + pub(crate) async fn execute(self) -> anyhow::Result<()> { + Cli.intro("Add a new pallet to your runtime")?; + let mut cmd = Command::new(""); + + let runtime_path = if let Some(path) = &self.runtime_path { + rustilities::paths::prefix_with_current_dir(path) + } else { + let working_dir = match env::current_dir() { + Ok(working_dir) => working_dir, + _ => cmd.error(ErrorKind::Io, "Cannot modify the working crate").exit(), + }; + // Give the chance to use the command either from a workspace containing a runtime or + // from a runtime crate if path not specified + if working_dir.join("runtime").exists() { + rustilities::paths::prefix_with_current_dir(working_dir.join("runtime")) + } else { + rustilities::paths::prefix_with_current_dir(working_dir) + } + }; + + if !manifest::is_runtime_crate(&runtime_path) { + cmd.error( + ErrorKind::InvalidValue, + "Make sure to run this command either in a workspace containing a runtime crate/a runtime crate or to specify the path to the runtime crate using -r.", + ) + .exit(); + } + + let spinner = cliclack::spinner(); + spinner.start("Updating runtime..."); + + let pallets = if self.pallets.is_empty() { + multiselect_pick!( + common_pallets::CommonPallets, + "Select the pallets you want to include in your runtime" + ) + } else { + self.pallets + }; + + let mut rollback = Rollback::default(); + + let mut precomputed_pallet_config_paths = Vec::with_capacity(pallets.len()); + + let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = + rust_writer_helpers::compute_pallet_related_paths(&runtime_path); + + let runtime_manifest = rustilities::manifest::find_innermost_manifest(&runtime_path) + .expect("Runtime is a crate, so it contains a manifest; qed;"); + + for pallet in pallets.iter() { + let pallet_name = + pallet.get_crate_name().splitn(2, '-').nth(1).unwrap_or("pallet").to_string(); + precomputed_pallet_config_paths + .push(configs_folder_path.join(format!("{}.rs", pallet_name))); + } + + rollback.note_file(&runtime_lib_path)?; + rollback.note_file(&runtime_manifest)?; + if let Some(ref pallet_impl_path) = self.pallet_impl_path { + // The impl path may be the runtime lib, so the path may be already noted. + match rollback.note_file(pallet_impl_path) { + Ok(()) => (), + Err(fs_rollback::Error::AlreadyNoted(_)) => (), + Err(err) => return Err(err.into()), + } + } + + for (index, pallet) in pallets.iter().enumerate() { + let pallet_name = + pallet.get_crate_name().splitn(2, '-').nth(1).unwrap_or("pallet").to_string(); + + let pallet_config_path = &precomputed_pallet_config_paths[index]; + + let roll_pallet_impl_path = match self.pallet_impl_path { + // specified impl path, so get it. + Some(ref pallet_impl_path) => rollback.get_noted_file(pallet_impl_path).unwrap_or( + rollback + .get_noted_file(&runtime_lib_path) + .expect("The file has been noted above;qed;"), + ), + None => { + rollback = rust_writer_helpers::compute_new_pallet_impl_path( + rollback, + &runtime_lib_path, + &configs_rs_path, + &configs_folder_path, + &configs_mod_path, + &pallet_config_path, + &pallet_name, + )?; + + rollback + .get_new_file(&pallet_config_path) + .expect("compute_new_pallet_impl_path noted this file; qed;") + }, + }; + + let roll_runtime_lib_path = rollback + .get_noted_file(&runtime_lib_path) + .expect("This file is noted by the rollback; qed;"); + let roll_manifest = rollback + .get_noted_file(&runtime_manifest) + .expect("This file is noted by the rollback; qed;"); + + // Add the pallet to the runtime module + let construct_runtime_preserver = Preserver::new("construct_runtime!"); + let mod_runtime_preserver = Preserver::new("mod runtime"); + let mut preserved_ast = rust_writer::preserver::preserve_and_parse( + roll_runtime_lib_path, + &[&construct_runtime_preserver, &mod_runtime_preserver], + )?; + + // Parse the runtime to find which of the runtime macros is being used and the highest + // pallet index used (if needed, otherwise 0). + let used_macro = rust_writer_helpers::find_used_runtime_macro(&preserved_ast)?; + match used_macro { + RuntimeUsedMacro::Runtime => { + let highest_index = + rust_writer_helpers::find_highest_pallet_index(&preserved_ast)?; + let pallet_to_runtime_implementor: ItemToMod = + ("runtime", pallet.get_pallet_declaration_runtime_module(highest_index)) + .into(); + + let mut finder = Finder::default().to_find(&pallet_to_runtime_implementor); + let pallet_already_present = finder.find(&preserved_ast); + if pallet_already_present { + return Err(anyhow::anyhow!(format!( + "{} is already in use.", + pallet.get_crate_name() + ))); + } else { + let mut mutator = + Mutator::default().to_mutate(&pallet_to_runtime_implementor); + mutator.mutate(&mut preserved_ast)?; + rust_writer::preserver::resolve_preserved( + &preserved_ast, + roll_runtime_lib_path, + )?; + } + }, + RuntimeUsedMacro::ConstructRuntime => { + let pallet_to_construct_runtime_implementor: TokenStreamToMacro = ( + parse_quote!(construct_runtime), + Some(parse_quote!(Runtime)), + pallet.get_pallet_declaration_construct_runtime(), + ) + .into(); + let mut finder = + Finder::default().to_find(&pallet_to_construct_runtime_implementor); + let pallet_already_present = finder.find(&preserved_ast); + if pallet_already_present { + return Err(anyhow::anyhow!(format!( + "{} is already in use.", + pallet.get_crate_name() + ))); + } else { + let mut mutator = + Mutator::default().to_mutate(&pallet_to_construct_runtime_implementor); + mutator.mutate(&mut preserved_ast)?; + rust_writer::preserver::resolve_preserved( + &preserved_ast, + roll_runtime_lib_path, + )?; + } + }, + } + + // Add the pallet impl block and its related use statements + let use_preserver = Preserver::new("use"); + let pub_use_preserver = Preserver::new("pub use"); + + let mut preserved_ast = rust_writer::preserver::preserve_and_parse( + roll_pallet_impl_path, + &[&use_preserver, &pub_use_preserver], + )?; + + for use_statement in pallet.get_impl_needed_use_statements() { + let use_statement: ItemToFile = use_statement.into(); + let mut finder = Finder::default().to_find(&use_statement); + let use_statement_used = finder.find(&preserved_ast); + if !use_statement_used { + let mut mutator = Mutator::default().to_mutate(&use_statement); + mutator.mutate(&mut preserved_ast)?; + } + } + + let pallet_impl_block_implementor: PalletImplBlockImplementor = ( + ItemToFile { item: pallet.get_needed_parameter_types() }, + ItemToFile { item: pallet.get_needed_impl_block() }, + ) + .into(); + + let mut mutator: PalletImplBlockImplementorMutatorWrapper = + Mutator::default().to_mutate(&pallet_impl_block_implementor).into(); + + mutator.mutate(&mut preserved_ast, None)?; + + rust_writer::preserver::resolve_preserved(&preserved_ast, roll_pallet_impl_path)?; + + // Update the crate's manifest to add the pallet crate + rustilities::manifest::add_crate_to_dependencies( + roll_manifest, + &pallet.get_crate_name(), + ManifestDependencyConfig::new( + ManifestDependencyOrigin::crates_io(&pallet.get_version()), + false, + vec![], + false, + ), + )?; + } + + rollback.commit()?; + + if let Some(mut workspace_toml) = + rustilities::manifest::find_workspace_manifest(&runtime_path) + { + workspace_toml.pop(); + rustilities::fmt::format_dir(&workspace_toml)?; + } else { + rustilities::fmt::format_dir(&runtime_path)?; + } + + spinner.stop("Your runtime has been updated and it's ready to use 🚀"); + Ok(()) + } +} diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs new file mode 100644 index 000000000..931c52a37 --- /dev/null +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-3.0 +use clap::ValueEnum; +use proc_macro2::{Literal, TokenStream}; +use strum_macros::{EnumIter, EnumMessage}; +use syn::{parse_quote, Item}; + +#[derive(Debug, Copy, Clone, PartialEq, EnumIter, EnumMessage, ValueEnum)] +pub enum CommonPallets { + /// Add pallet-balances to your runtime. + #[strum(message = "balances", detailed_message = "Add pallet-balances to your runtime.")] + Balances, + /// Add pallet-contracts to your runtime. + #[strum(message = "contracts", detailed_message = "Add pallet-contracts to your runtime.")] + Contracts, +} + +impl CommonPallets { + pub fn get_crate_name(&self) -> String { + match self { + CommonPallets::Balances => "pallet-balances".to_string(), + CommonPallets::Contracts => "pallet-contracts".to_string(), + } + } + + pub fn get_pallet_declaration_construct_runtime(&self) -> TokenStream { + match self { + CommonPallets::Balances => parse_quote! { Balances: pallet_balances, }, + CommonPallets::Contracts => parse_quote! { Contracts: pallet_contracts, }, + } + } + + pub fn get_pallet_declaration_runtime_module(&self, highest_index: Literal) -> Item { + match self { + CommonPallets::Balances => parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(#highest_index)] + pub type Balances = pallet_balances; + }, + CommonPallets::Contracts => parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(#highest_index)] + pub type Contracts = pallet_contracts; + }, + } + } + + pub fn get_impl_needed_use_statements(&self) -> Vec { + match self { + CommonPallets::Balances => vec![parse_quote!( + use crate::System; + )], + CommonPallets::Contracts => vec![parse_quote!( + use crate::Balances; + )], + } + } + + pub fn get_needed_parameter_types(&self) -> Item { + match self { + CommonPallets::Balances => parse_quote!(), + CommonPallets::Contracts => parse_quote! { + parameter_types!{ + pub Schedule: pallet_contracts::Schedule = Default::default(); + } + }, + } + } + + pub fn get_needed_impl_block(&self) -> Item { + match self { + CommonPallets::Balances => parse_quote! { + ///TEMP_DOC + #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] + impl pallet_balances::Config for Runtime{ + type AccountStore = System; + } + }, + CommonPallets::Contracts => parse_quote! { + ///TEMP_DOC + #[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] + impl pallet_contracts::Config for Runtime{ + type Currency = Balances; + type Schedule = [pallet_contracts::Frame; 5]; + type CallStack = Schedule; + } + }, + } + } +} diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index 423a24e9a..d2b301dd0 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -12,6 +12,8 @@ use crate::{ }; use clap::Subcommand; use std::fmt::{Display, Formatter, Result}; + +pub(crate) mod add; #[cfg(feature = "parachain")] pub(crate) mod bench; pub(crate) mod build; @@ -52,6 +54,9 @@ pub(crate) enum Command { /// Remove generated/cached artifacts. #[clap(alias = "C")] Clean(clean::CleanArgs), + /// Add a new feature to your existing polkadot-sdk project + #[clap(name = "add", alias = "a")] + Add(add::AddArgs), } /// Help message for the build command. @@ -180,6 +185,9 @@ impl Command { }, } }, + Self::Add(args) => match args.command { + add::Command::Pallet(cmd) => cmd.execute().await.map(|_| Null), + }, } } } @@ -211,6 +219,7 @@ impl Display for Command { Self::Clean(_) => write!(f, "clean"), #[cfg(feature = "parachain")] Self::Bench(args) => write!(f, "bench {}", args.command), + Self::Add(args) => write!(f, "new {}", args.command) } } } From 2b23afaa8305de079195f71cd1f5f51aa366a853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sat, 12 Apr 2025 17:26:33 +0200 Subject: [PATCH 03/38] Finishing pop add pallet command + tests --- Cargo.lock | 36 ++- Cargo.toml | 3 +- crates/pop-cli/Cargo.toml | 2 + crates/pop-cli/src/commands/add/mod.rs | 2 +- crates/pop-cli/src/commands/add/pallet.rs | 152 +++++----- .../src/commands/add/pallet/common_pallets.rs | 267 +++++++++++++++++- crates/pop-cli/src/commands/mod.rs | 2 +- crates/pop-cli/tests/parachain.rs | 116 +++++++- crates/pop-common/src/helpers.rs | 17 +- 9 files changed, 495 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6562b7195..162d48cd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1330,6 +1330,16 @@ dependencies = [ "toml 0.8.19", ] +[[package]] +name = "cargo_toml" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fbd1fe9db3ebf71b89060adaf7b0504c2d6a425cf061313099547e382c2e472" +dependencies = [ + "serde", + "toml 0.8.19", +] + [[package]] name = "cc" version = "1.2.16" @@ -4252,7 +4262,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -5633,7 +5643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -8121,6 +8131,8 @@ dependencies = [ "proc-macro2", "reqwest", "rust_writer", + "rustilities", + "semver", "serde", "serde_json", "similar", @@ -8145,7 +8157,7 @@ name = "pop-common" version = "0.7.0" dependencies = [ "anyhow", - "cargo_toml", + "cargo_toml 0.20.5", "contract-build", "contract-extrinsics", "duct", @@ -9266,13 +9278,15 @@ dependencies = [ [[package]] name = "rustilities" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d049a83871f271465eba32e54bc8b9342795ac9675015dd9891938e6fec7f75" +checksum = "f6c4a622609b034008b203bd62d1500f1e03889758aed046ddd68337a98c8cbf" dependencies = [ + "cargo_toml 0.21.0", "proc-macro2", "syn 2.0.100", "thiserror 2.0.11", + "toml_edit", ] [[package]] @@ -10935,9 +10949,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] @@ -13864,9 +13878,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap 2.8.0", "serde", @@ -15435,9 +15449,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 33992c330..eb68d9127 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,9 @@ glob = "0.3.1" log = "0.4.20" mockito = "1.4.0" proc-macro2 = "1.0.86" -rustilities = "2.2.0" +rustilities = "2.2.1" rust_writer = "1.0.4" +semver = "1.0.26" syn = "2.0.100" tar = "0.4.40" tempfile = "3.10" diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index 878e5f975..60fa2f00d 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -20,6 +20,7 @@ fs_rollback.workspace = true os_info.workspace = true proc-macro2.workspace = true reqwest.workspace = true +semver.workspace = true rustilities = { workspace = true, features = ["fmt", "manifest"]} rust_writer.workspace = true serde = { workspace = true, features = ["derive"] } @@ -70,6 +71,7 @@ frame-try-runtime = { workspace = true, features = ["try-runtime"] } assert_cmd.workspace = true contract-extrinsics.workspace = true mockito.workspace = true +rustilities = { workspace = true, features = ["fmt", "manifest", "parsing"]} subxt.workspace = true subxt-signer.workspace = true sp-weights.workspace = true diff --git a/crates/pop-cli/src/commands/add/mod.rs b/crates/pop-cli/src/commands/add/mod.rs index 8f9088e26..d78bf1152 100644 --- a/crates/pop-cli/src/commands/add/mod.rs +++ b/crates/pop-cli/src/commands/add/mod.rs @@ -12,7 +12,7 @@ pub struct AddArgs { pub command: Command, } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum Command { /// Add a new pallet to an existing runtime #[clap(alias = "P")] diff --git a/crates/pop-cli/src/commands/add/pallet.rs b/crates/pop-cli/src/commands/add/pallet.rs index 1646e23a4..449b82086 100644 --- a/crates/pop-cli/src/commands/add/pallet.rs +++ b/crates/pop-cli/src/commands/add/pallet.rs @@ -2,15 +2,11 @@ use crate::{ cli::{traits::Cli as _, Cli}, - multiselect_pick, + common::writer::{self, RuntimeUsedMacro}, }; use clap::{error::ErrorKind, Args, Command}; -use cliclack::multiselect; +use common_pallets::{InputPallet, InputPalletParser}; use fs_rollback::Rollback; -use pop_common::{ - manifest, - rust_writer_helpers::{self, RuntimeUsedMacro}, -}; use rust_writer::{ ast::{ finder, @@ -23,7 +19,7 @@ use rust_writer::{ }; use rustilities::manifest::{ManifestDependencyConfig, ManifestDependencyOrigin}; use std::{env, path::PathBuf}; -use strum::{EnumMessage, IntoEnumIterator}; +use strum::EnumMessage; use syn::{parse_quote, visit_mut::VisitMut}; mod common_pallets; @@ -35,24 +31,37 @@ struct PalletImplBlockImplementor; #[derive(Args, Debug, Clone)] pub struct AddPalletCommand { - #[arg(short, long, value_enum, num_args(1..), required = true, help = "The pallets you want to include to your runtime.")] - pub(crate) pallets: Vec, - #[arg(short, long, help = "Specify the path to the runtime crate.")] + #[arg( + long, + short, + num_args(1..), + required = true, + value_parser = InputPalletParser, + help = "The pallets added to the runtime. The input should follow the format =, where is one of the options described below." + )] + pub(crate) pallets: Vec, + #[arg( + short, + long, + help = "pop add pallet should be called from a runtime crate or from a workspace containing a runtime crate. If this command is called from somewhere else, this argument allows to specify the path to the runtime crate." + )] pub(crate) runtime_path: Option, #[arg( long, - help = "Pop-Cli will place the impl blocks for your pallets' Config traits inside a dedicated file under configs directory. Use this argument to point to other path." + help = "pop add pallet will place the impl blocks for your pallets' Config traits inside a dedicated file under the configs directory. Use this argument to place them somewhere else." )] pub(crate) pallet_impl_path: Option, } +const INVALID_DIR_MSG: &str = "Make sure to run this command either in a runtime crate contained in a workspace, in the workspace itself or to specify the path to the runtime crate using -r."; + impl AddPalletCommand { pub(crate) async fn execute(self) -> anyhow::Result<()> { Cli.intro("Add a new pallet to your runtime")?; let mut cmd = Command::new(""); let runtime_path = if let Some(path) = &self.runtime_path { - rustilities::paths::prefix_with_current_dir(path) + pop_common::helpers::prefix_with_current_dir_if_needed(&path) } else { let working_dir = match env::current_dir() { Ok(working_dir) => working_dir, @@ -61,51 +70,50 @@ impl AddPalletCommand { // Give the chance to use the command either from a workspace containing a runtime or // from a runtime crate if path not specified if working_dir.join("runtime").exists() { - rustilities::paths::prefix_with_current_dir(working_dir.join("runtime")) + pop_common::helpers::prefix_with_current_dir_if_needed(working_dir.join("runtime")) } else { - rustilities::paths::prefix_with_current_dir(working_dir) + pop_common::helpers::prefix_with_current_dir_if_needed(&working_dir) } }; - if !manifest::is_runtime_crate(&runtime_path) { - cmd.error( - ErrorKind::InvalidValue, - "Make sure to run this command either in a workspace containing a runtime crate/a runtime crate or to specify the path to the runtime crate using -r.", - ) - .exit(); + let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = + writer::compute_pallet_related_paths(&runtime_path); + + let runtime_lib_content = std::fs::read_to_string(&runtime_lib_path)?; + + if !runtime_lib_content.contains("construct_runtime!") && + !runtime_lib_content.contains("mod runtime") + { + cmd.error(ErrorKind::InvalidValue, INVALID_DIR_MSG).exit(); } let spinner = cliclack::spinner(); spinner.start("Updating runtime..."); - let pallets = if self.pallets.is_empty() { - multiselect_pick!( - common_pallets::CommonPallets, - "Select the pallets you want to include in your runtime" - ) - } else { - self.pallets - }; - let mut rollback = Rollback::default(); - let mut precomputed_pallet_config_paths = Vec::with_capacity(pallets.len()); + let mut precomputed_pallet_config_paths = Vec::with_capacity(self.pallets.len()); - let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = - rust_writer_helpers::compute_pallet_related_paths(&runtime_path); - - let runtime_manifest = rustilities::manifest::find_innermost_manifest(&runtime_path) - .expect("Runtime is a crate, so it contains a manifest; qed;"); + for pallet in self.pallets.iter() { + let InputPallet { pallet, .. } = pallet; - for pallet in pallets.iter() { - let pallet_name = - pallet.get_crate_name().splitn(2, '-').nth(1).unwrap_or("pallet").to_string(); + let pallet_name = pallet.get_message().expect( + "All pallets in common_pallets::CommonPallets have a defined message; qed;", + ); precomputed_pallet_config_paths .push(configs_folder_path.join(format!("{}.rs", pallet_name))); } + let runtime_manifest = rustilities::manifest::find_innermost_manifest(&runtime_path) + .ok_or(anyhow::anyhow!(INVALID_DIR_MSG))?; + + let workspace_manifest = pop_common::find_workspace_toml(&runtime_path) + .ok_or(anyhow::anyhow!(INVALID_DIR_MSG))?; + rollback.note_file(&runtime_lib_path)?; rollback.note_file(&runtime_manifest)?; + rollback.note_file(&workspace_manifest)?; + if let Some(ref pallet_impl_path) = self.pallet_impl_path { // The impl path may be the runtime lib, so the path may be already noted. match rollback.note_file(pallet_impl_path) { @@ -115,21 +123,21 @@ impl AddPalletCommand { } } - for (index, pallet) in pallets.iter().enumerate() { - let pallet_name = - pallet.get_crate_name().splitn(2, '-').nth(1).unwrap_or("pallet").to_string(); + for (index, pallet) in self.pallets.iter().enumerate() { + let InputPallet { pallet, version } = pallet; + + let pallet_name = pallet.get_message().expect( + "All pallets in common_pallets::CommonPallets have a defined message; qed;", + ); let pallet_config_path = &precomputed_pallet_config_paths[index]; let roll_pallet_impl_path = match self.pallet_impl_path { - // specified impl path, so get it. - Some(ref pallet_impl_path) => rollback.get_noted_file(pallet_impl_path).unwrap_or( - rollback - .get_noted_file(&runtime_lib_path) - .expect("The file has been noted above;qed;"), - ), + Some(ref pallet_impl_path) => rollback + .get_noted_file(pallet_impl_path) + .expect("The file has been noted above;qed;"), None => { - rollback = rust_writer_helpers::compute_new_pallet_impl_path( + rollback = writer::create_new_pallet_impl_path_structure( rollback, &runtime_lib_path, &configs_rs_path, @@ -141,16 +149,21 @@ impl AddPalletCommand { rollback .get_new_file(&pallet_config_path) - .expect("compute_new_pallet_impl_path noted this file; qed;") + .expect("create_new_pallet_impl_path_structure noted this file; qed;") }, }; let roll_runtime_lib_path = rollback .get_noted_file(&runtime_lib_path) - .expect("This file is noted by the rollback; qed;"); - let roll_manifest = rollback + .expect("The file has been noted above; qed;"); + + let roll_runtime_manifest = rollback .get_noted_file(&runtime_manifest) - .expect("This file is noted by the rollback; qed;"); + .expect("The file has been noted above; qed;"); + + let roll_workspace_manifest = rollback + .get_noted_file(&workspace_manifest) + .expect("The file has been noted above; qed;"); // Add the pallet to the runtime module let construct_runtime_preserver = Preserver::new("construct_runtime!"); @@ -162,18 +175,16 @@ impl AddPalletCommand { // Parse the runtime to find which of the runtime macros is being used and the highest // pallet index used (if needed, otherwise 0). - let used_macro = rust_writer_helpers::find_used_runtime_macro(&preserved_ast)?; + let used_macro = writer::find_used_runtime_macro(&preserved_ast)?; match used_macro { RuntimeUsedMacro::Runtime => { - let highest_index = - rust_writer_helpers::find_highest_pallet_index(&preserved_ast)?; + let highest_index = writer::find_highest_pallet_index(&preserved_ast)?; let pallet_to_runtime_implementor: ItemToMod = ("runtime", pallet.get_pallet_declaration_runtime_module(highest_index)) .into(); let mut finder = Finder::default().to_find(&pallet_to_runtime_implementor); - let pallet_already_present = finder.find(&preserved_ast); - if pallet_already_present { + if finder.find(&preserved_ast) { return Err(anyhow::anyhow!(format!( "{} is already in use.", pallet.get_crate_name() @@ -197,8 +208,7 @@ impl AddPalletCommand { .into(); let mut finder = Finder::default().to_find(&pallet_to_construct_runtime_implementor); - let pallet_already_present = finder.find(&preserved_ast); - if pallet_already_present { + if finder.find(&preserved_ast) { return Err(anyhow::anyhow!(format!( "{} is already in use.", pallet.get_crate_name() @@ -227,8 +237,7 @@ impl AddPalletCommand { for use_statement in pallet.get_impl_needed_use_statements() { let use_statement: ItemToFile = use_statement.into(); let mut finder = Finder::default().to_find(&use_statement); - let use_statement_used = finder.find(&preserved_ast); - if !use_statement_used { + if !finder.find(&preserved_ast) { let mut mutator = Mutator::default().to_mutate(&use_statement); mutator.mutate(&mut preserved_ast)?; } @@ -247,12 +256,23 @@ impl AddPalletCommand { rust_writer::preserver::resolve_preserved(&preserved_ast, roll_pallet_impl_path)?; - // Update the crate's manifest to add the pallet crate + // Update the manifests to add the pallet crate + rustilities::manifest::add_crate_to_dependencies( + roll_workspace_manifest, + &pallet.get_crate_name(), + ManifestDependencyConfig::new( + ManifestDependencyOrigin::crates_io(&version), + false, + vec![], + false, + ), + )?; + rustilities::manifest::add_crate_to_dependencies( - roll_manifest, + roll_runtime_manifest, &pallet.get_crate_name(), ManifestDependencyConfig::new( - ManifestDependencyOrigin::crates_io(&pallet.get_version()), + ManifestDependencyOrigin::workspace(), false, vec![], false, @@ -262,9 +282,7 @@ impl AddPalletCommand { rollback.commit()?; - if let Some(mut workspace_toml) = - rustilities::manifest::find_workspace_manifest(&runtime_path) - { + if let Some(mut workspace_toml) = pop_common::manifest::find_workspace_toml(&runtime_path) { workspace_toml.pop(); rustilities::fmt::format_dir(&workspace_toml)?; } else { diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 931c52a37..c21cad238 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -1,11 +1,16 @@ // SPDX-License-Identifier: GPL-3.0 -use clap::ValueEnum; +use clap::{ + builder::{PossibleValue, TypedValueParser}, + ValueEnum, +}; use proc_macro2::{Literal, TokenStream}; +use semver::Version; +use std::{ffi::OsStr, str::FromStr}; use strum_macros::{EnumIter, EnumMessage}; use syn::{parse_quote, Item}; #[derive(Debug, Copy, Clone, PartialEq, EnumIter, EnumMessage, ValueEnum)] -pub enum CommonPallets { +pub(crate) enum CommonPallets { /// Add pallet-balances to your runtime. #[strum(message = "balances", detailed_message = "Add pallet-balances to your runtime.")] Balances, @@ -15,21 +20,21 @@ pub enum CommonPallets { } impl CommonPallets { - pub fn get_crate_name(&self) -> String { + pub(crate) fn get_crate_name(&self) -> String { match self { CommonPallets::Balances => "pallet-balances".to_string(), CommonPallets::Contracts => "pallet-contracts".to_string(), } } - pub fn get_pallet_declaration_construct_runtime(&self) -> TokenStream { + pub(crate) fn get_pallet_declaration_construct_runtime(&self) -> TokenStream { match self { CommonPallets::Balances => parse_quote! { Balances: pallet_balances, }, CommonPallets::Contracts => parse_quote! { Contracts: pallet_contracts, }, } } - pub fn get_pallet_declaration_runtime_module(&self, highest_index: Literal) -> Item { + pub(crate) fn get_pallet_declaration_runtime_module(&self, highest_index: Literal) -> Item { match self { CommonPallets::Balances => parse_quote! { ///TEMP_DOC @@ -44,29 +49,32 @@ impl CommonPallets { } } - pub fn get_impl_needed_use_statements(&self) -> Vec { + pub(crate) fn get_impl_needed_use_statements(&self) -> Vec { match self { CommonPallets::Balances => vec![parse_quote!( + ///TEMP_DOC use crate::System; )], CommonPallets::Contracts => vec![parse_quote!( + ///TEMP_DOC use crate::Balances; )], } } - pub fn get_needed_parameter_types(&self) -> Item { + pub(crate) fn get_needed_parameter_types(&self) -> Item { match self { - CommonPallets::Balances => parse_quote!(), + CommonPallets::Balances => Item::Verbatim(TokenStream::new()), CommonPallets::Contracts => parse_quote! { - parameter_types!{ - pub Schedule: pallet_contracts::Schedule = Default::default(); - } - }, + ///TEMP_DOC + parameter_types!{ + pub Schedule: pallet_contracts::Schedule = Default::default(); + } + }, } } - pub fn get_needed_impl_block(&self) -> Item { + pub(crate) fn get_needed_impl_block(&self) -> Item { match self { CommonPallets::Balances => parse_quote! { ///TEMP_DOC @@ -87,3 +95,236 @@ impl CommonPallets { } } } + +impl FromStr for CommonPallets { + type Err = String; + + fn from_str(input: &str) -> Result { + match input.to_lowercase().as_str() { + "balances" => Ok(CommonPallets::Balances), + "contracts" => Ok(CommonPallets::Contracts), + _ => Err(format!("'{}' is not a valid pallet.", input)), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct InputPallet { + pub(crate) pallet: CommonPallets, + pub(crate) version: String, +} + +impl FromStr for InputPallet { + type Err = String; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split('=').collect(); + if parts.len() != 2 { + return Err(format!("Invalid format: expected =, got '{}'", s)); + } + + let pallet = parts[0] + .parse::() + .map_err(|_| format!("Invalid pallet: '{}'.", parts[0]))?; + + // Not interested in using the Version type at all, just need to know if this &str can be + // parsed as Version + let _ = parts[1] + .parse::() + .map_err(|e| format!("Invalid version '{}': {}", parts[1], e))?; + + Ok(InputPallet { pallet, version: parts[1].to_owned() }) + } +} + +#[derive(Clone)] +pub(crate) struct InputPalletParser; + +impl TypedValueParser for InputPalletParser { + type Value = InputPallet; + + fn parse_ref( + &self, + _cmd: &clap::Command, + _arg: Option<&clap::Arg>, + value: &OsStr, + ) -> Result { + let s = value.to_string_lossy(); + s.parse::() + .map_err(|err_msg| clap::Error::raw(clap::error::ErrorKind::InvalidValue, err_msg)) + } + + fn possible_values(&self) -> Option + '_>> { + let iter = CommonPallets::value_variants() + .iter() + .map(|variant| variant.to_possible_value().expect("value should be possible")); + Some(Box::new(iter)) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn get_crate_name_works() { + assert_eq!(CommonPallets::Balances.get_crate_name(), "pallet-balances"); + assert_eq!(CommonPallets::Contracts.get_crate_name(), "pallet-contracts"); + } + + #[test] + fn get_pallet_declaration_construct_runtime_works() { + assert!(rustilities::parsing::syntactic_token_stream_compare( + CommonPallets::Balances.get_pallet_declaration_construct_runtime(), + parse_quote! { Balances: pallet_balances, } + )); + + assert!(rustilities::parsing::syntactic_token_stream_compare( + CommonPallets::Contracts.get_pallet_declaration_construct_runtime(), + parse_quote! { Contracts: pallet_contracts, } + )); + } + + #[test] + fn get_pallet_declaration_runtime_module_works() { + assert_eq!( + CommonPallets::Balances.get_pallet_declaration_runtime_module(parse_quote!(1)), + parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(1)] + pub type Balances = pallet_balances; + } + ); + assert_eq!( + CommonPallets::Contracts.get_pallet_declaration_runtime_module(parse_quote!(1)), + parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(1)] + pub type Contracts = pallet_contracts; + } + ); + } + + #[test] + fn get_impl_needed_use_statements_works() { + assert_eq!( + CommonPallets::Balances.get_impl_needed_use_statements()[0], + parse_quote! { + ///TEMP_DOC + use crate::System; + } + ); + assert_eq!( + CommonPallets::Contracts.get_impl_needed_use_statements()[0], + parse_quote! { + ///TEMP_DOC + use crate::Balances; + } + ); + } + + #[test] + fn get_needed_parameter_types_works() { + assert_eq!( + CommonPallets::Balances.get_needed_parameter_types(), + Item::Verbatim(TokenStream::new()) + ); + + assert_eq!( + CommonPallets::Contracts.get_needed_parameter_types(), + parse_quote! { + ///TEMP_DOC + parameter_types!{ + pub Schedule: pallet_contracts::Schedule = Default::default(); + } + } + ); + } + + #[test] + fn get_needed_impl_block_works() { + assert_eq!( + CommonPallets::Balances.get_needed_impl_block(), + parse_quote! { + ///TEMP_DOC + #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] + impl pallet_balances::Config for Runtime { + type AccountStore = System; + } + } + ); + + assert_eq!( + CommonPallets::Contracts.get_needed_impl_block(), + parse_quote! { + ///TEMP_DOC + #[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] + impl pallet_contracts::Config for Runtime { + type Currency = Balances; + type Schedule = [pallet_contracts::Frame; 5]; + type CallStack = Schedule; + } + } + ); + } + + #[test] + fn common_pallets_from_str_works() { + assert_eq!("balances".parse::().unwrap(), CommonPallets::Balances); + assert_eq!("contracts".parse::().unwrap(), CommonPallets::Contracts); + assert!("invalid".parse::().is_err()); + } + + #[test] + fn input_pallet_from_str_valid_works() { + let input: InputPallet = "balances=1.0.0".parse().unwrap(); + assert_eq!(input.pallet, CommonPallets::Balances); + assert_eq!(input.version, "1.0.0"); + } + + #[test] + fn input_pallet_from_str_invalid_format_fails() { + assert!("balances-1.0.0".parse::().is_err()); + } + + #[test] + fn input_pallet_from_str_invalid_pallet_fails() { + assert!("notapallet=1.0.0".parse::().is_err()); + } + + #[test] + fn input_pallet_from_str_invalid_version_fails() { + assert!("balances=invalid".parse::().is_err()); + } + + #[test] + fn input_pallet_from_str_missing_parts_fails() { + assert!("balances=".parse::().is_err()); + assert!("=1.0.0".parse::().is_err()); + assert!("balances".parse::().is_err()); + assert!("=".parse::().is_err()); + } + + #[test] + fn input_pallet_parser_parse_ref_works() { + let cmd = clap::Command::new("testcmd"); + let arg = clap::Arg::new("pallet"); + let parsed = InputPalletParser + .parse_ref(&cmd, Some(&arg), OsStr::new("contracts=2.0.0")) + .unwrap(); + assert_eq!(parsed.pallet, CommonPallets::Contracts); + assert_eq!(parsed.version, "2.0.0"); + } + + #[test] + fn input_pallet_parser_possible_values_works() { + let possible: Vec = InputPalletParser + .possible_values() + .unwrap() + .map(|pv| pv.get_name().to_owned()) + .collect(); + assert!(possible.contains(&"balances".to_owned())); + assert!(possible.contains(&"contracts".to_owned())); + } +} diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index d2b301dd0..cb8ba25e2 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -219,7 +219,7 @@ impl Display for Command { Self::Clean(_) => write!(f, "clean"), #[cfg(feature = "parachain")] Self::Bench(args) => write!(f, "bench {}", args.command), - Self::Add(args) => write!(f, "new {}", args.command) + Self::Add(args) => write!(f, "add {:?}", args.command), } } } diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 783793782..48b758d4b 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -8,7 +8,7 @@ use std::{fs, path::Path, process::Command as Cmd}; use strum::VariantArray; use tokio::time::{sleep, Duration}; -/// Test the parachain lifecycle: new, build, up, call. +/// Test the parachain lifecycle: new, add,build, up, call. #[tokio::test] async fn parachain_lifecycle() -> Result<()> { let temp = tempfile::tempdir().unwrap(); @@ -36,6 +36,120 @@ async fn parachain_lifecycle() -> Result<()> { .success(); assert!(temp_dir.join("test_parachain").exists()); + // pop add correctly adds pallet-contracts to the template + let test_parachain = tempdir.join("test_parachain"); + let runtime_path = test_parachain.join("runtime"); + + let workspace_manifest_path = test_parachain.join("Cargo.toml"); + let runtime_manifest_path = runtime_path.join("Cargo.toml"); + let runtime_lib_path = runtime_path.join("src").join("lib.rs"); + let pallet_configs_path = runtime_path.join("src").join("configs"); + let pallet_configs_mod_path = pallet_configs_path.join("mod.rs"); + let contracts_pallet_config_path = pallet_configs_path.join("contracts.rs"); + + assert!(!contracts_pallet_config_path.exists()); + + let runtime_lib_content_before = std::fs::read_to_string(&runtime_lib_path).unwrap(); + let pallet_configs_mod_content_before = + std::fs::read_to_string(&pallet_configs_mod_path).unwrap(); + let workspace_manifest_content_before = + std::fs::read_to_string(&workspace_manifest_path).unwrap(); + let runtime_manifest_content_before = std::fs::read_to_string(&runtime_manifest_path).unwrap(); + + Command::cargo_bin("pop") + .unwrap() + .current_dir(&test_parachain) + .args(&["add", "pallet", "-p", "contracts=40.0.1"]) + .assert() + .success(); + + let runtime_lib_content_after = std::fs::read_to_string(&runtime_lib_path).unwrap(); + let pallet_configs_mod_content_after = + std::fs::read_to_string(&pallet_configs_mod_path).unwrap(); + let workspace_manifest_content_after = + std::fs::read_to_string(&workspace_manifest_path).unwrap(); + let runtime_manifest_content_after = std::fs::read_to_string(&runtime_manifest_path).unwrap(); + let contracts_pallet_config_content = + std::fs::read_to_string(&contracts_pallet_config_path).unwrap(); + + let runtime_lib_diff = + TextDiff::from_lines(&runtime_lib_content_before, &runtime_lib_content_after); + let pallet_configs_mod_diff = + TextDiff::from_lines(&pallet_configs_mod_content_before, &pallet_configs_mod_content_after); + let workspace_manifest_diff = + TextDiff::from_lines(&workspace_manifest_content_before, &workspace_manifest_content_after); + let runtime_manifest_diff = + TextDiff::from_lines(&runtime_manifest_content_before, &runtime_manifest_content_after); + + let expected_inserted_lines_runtime_lib = vec![ + "\n", + " #[runtime::pallet_index(34)]\n", + " pub type Contracts = pallet_contracts;\n", + ]; + let expected_inserted_lines_configs_mod = vec!["mod contracts;\n"]; + let expected_inserted_lines_workspace_manifest = + vec!["pallet-contracts = { version = \"40.0.1\", default-features = false }\n"]; + let expected_inserted_lines_runtime_manifest = + vec!["pallet-contracts = { workspace = true, default-features = false }\n"]; + + let mut inserted_lines_runtime_lib = Vec::with_capacity(3); + let mut inserted_lines_configs_mod = Vec::with_capacity(1); + let mut inserted_lines_workspace_manifest = Vec::with_capacity(1); + let mut inserted_lines_runtime_manifest = Vec::with_capacity(1); + + for change in runtime_lib_diff.iter_all_changes() { + match change.tag() { + ChangeTag::Delete => panic!("no deletion expected"), + ChangeTag::Insert => inserted_lines_runtime_lib.push(change.value()), + _ => (), + } + } + + for change in pallet_configs_mod_diff.iter_all_changes() { + match change.tag() { + ChangeTag::Delete => panic!("no deletion expected"), + ChangeTag::Insert => inserted_lines_configs_mod.push(change.value()), + _ => (), + } + } + + for change in workspace_manifest_diff.iter_all_changes() { + match change.tag() { + ChangeTag::Delete => panic!("no deletion expected"), + ChangeTag::Insert => inserted_lines_workspace_manifest.push(change.value()), + _ => (), + } + } + + for change in runtime_manifest_diff.iter_all_changes() { + match change.tag() { + ChangeTag::Delete => panic!("no deletion expected"), + ChangeTag::Insert => inserted_lines_runtime_manifest.push(change.value()), + _ => (), + } + } + + assert_eq!(expected_inserted_lines_runtime_lib, inserted_lines_runtime_lib); + assert_eq!(expected_inserted_lines_configs_mod, inserted_lines_configs_mod); + assert_eq!(expected_inserted_lines_workspace_manifest, inserted_lines_workspace_manifest); + assert_eq!(expected_inserted_lines_runtime_manifest, inserted_lines_runtime_manifest); + + assert_eq!( + contracts_pallet_config_content, + r#"use crate::Balances; +parameter_types! { + pub Schedule : pallet_contracts::Schedule < Runtime > = Default::default(); +} + +#[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] +impl pallet_contracts::Config for Runtime { + type Currency = Balances; + type Schedule = [pallet_contracts::Frame; 5]; + type CallStack = Schedule; +} +"# + ); + // pop build --release --path "./test_parachain" Command::cargo_bin("pop") .unwrap() diff --git a/crates/pop-common/src/helpers.rs b/crates/pop-common/src/helpers.rs index 369b15bd2..77db26419 100644 --- a/crates/pop-common/src/helpers.rs +++ b/crates/pop-common/src/helpers.rs @@ -44,15 +44,18 @@ pub fn get_project_name_from_path<'a>(path: &'a Path, default: &'a str) -> &'a s /// /// # Arguments /// * `path` - The path to be prefixed if needed. -pub fn prefix_with_current_dir_if_needed(path: PathBuf) -> PathBuf { - let components = &path.components().collect::>(); - if !components.is_empty() { - // If the first component is a normal component, we prefix the path with the current dir - if let Component::Normal(_) = components[0] { - return as AsRef>::as_ref(&Component::CurDir).join(path); +pub fn prefix_with_current_dir_if_needed>(path: P) -> PathBuf { + fn do_prefix_with_current_dir(path: &Path) -> PathBuf { + let components = path.components().collect::>(); + if !components.is_empty() { + // If the first component is a normal component, we prefix the path with the current dir + if let Component::Normal(_) = components[0] { + return as AsRef>::as_ref(&Component::CurDir).join(path); + } } + path.to_path_buf() } - path + do_prefix_with_current_dir(path.as_ref()) } /// Returns the relative path from `base` to `full` if `full` is inside `base`. From 46877a44b3a71d604210eea4253e366a731e7c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sat, 12 Apr 2025 17:34:48 +0200 Subject: [PATCH 04/38] Merge dependencies --- crates/pop-cli/Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index 0c202e38d..649d6126e 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -29,12 +29,9 @@ rustilities = { workspace = true, features = ["fmt", "manifest"]} rust_writer.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true -<<<<<<< HEAD syn = { workspace = true, features = ["parsing"]} -======= strum.workspace = true strum_macros.workspace = true ->>>>>>> origin/main tempfile.workspace = true tokio.workspace = true toml.workspace = true From 12578da8f259cae944ec67c3bf8134c879cf3492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sat, 12 Apr 2025 17:50:09 +0200 Subject: [PATCH 05/38] Add use statements to test --- crates/pop-cli/tests/parachain.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 48b758d4b..edb35dfa5 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -7,6 +7,7 @@ use pop_parachains::Parachain; use std::{fs, path::Path, process::Command as Cmd}; use strum::VariantArray; use tokio::time::{sleep, Duration}; +use similar::{TextDiff, ChangeTag}; /// Test the parachain lifecycle: new, add,build, up, call. #[tokio::test] @@ -37,7 +38,7 @@ async fn parachain_lifecycle() -> Result<()> { assert!(temp_dir.join("test_parachain").exists()); // pop add correctly adds pallet-contracts to the template - let test_parachain = tempdir.join("test_parachain"); + let test_parachain = temp_dir.join("test_parachain"); let runtime_path = test_parachain.join("runtime"); let workspace_manifest_path = test_parachain.join("Cargo.toml"); From 1c0e66f159bb2d2094f3135b98097b795f764407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sat, 12 Apr 2025 17:51:25 +0200 Subject: [PATCH 06/38] Fix clippy --- crates/pop-parachains/src/new_parachain.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/pop-parachains/src/new_parachain.rs b/crates/pop-parachains/src/new_parachain.rs index 7d3ebd067..b5219e1fe 100644 --- a/crates/pop-parachains/src/new_parachain.rs +++ b/crates/pop-parachains/src/new_parachain.rs @@ -39,6 +39,7 @@ pub fn instantiate_template_dir( Ok(tag) } +/// Instantiate a standard template. pub fn instantiate_standard_template( template: &Parachain, target: &Path, @@ -79,6 +80,7 @@ pub fn instantiate_standard_template( Ok(tag) } +/// Instantiate open zeppelin template. pub fn instantiate_openzeppelin_template( template: &Parachain, target: &Path, From 9c53e83d2295b40526487c887c60602fe52c1f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sat, 12 Apr 2025 17:52:23 +0200 Subject: [PATCH 07/38] fmt --- crates/pop-cli/tests/parachain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index edb35dfa5..ffeb48efe 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -4,10 +4,10 @@ use anyhow::Result; use assert_cmd::{cargo::cargo_bin, Command}; use pop_common::{find_free_port, templates::Template}; use pop_parachains::Parachain; +use similar::{ChangeTag, TextDiff}; use std::{fs, path::Path, process::Command as Cmd}; use strum::VariantArray; use tokio::time::{sleep, Duration}; -use similar::{TextDiff, ChangeTag}; /// Test the parachain lifecycle: new, add,build, up, call. #[tokio::test] From edbb1ba42997e583c482685187d6124cfa03e4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:10:26 +0200 Subject: [PATCH 08/38] Fix test --- crates/pop-cli/tests/parachain.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index ffeb48efe..58cfbe441 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -138,6 +138,7 @@ async fn parachain_lifecycle() -> Result<()> { assert_eq!( contracts_pallet_config_content, r#"use crate::Balances; + parameter_types! { pub Schedule : pallet_contracts::Schedule < Runtime > = Default::default(); } From 149f21d1d7e3c5dcf67d2bf47f2ecbad8a669bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:24:27 +0200 Subject: [PATCH 09/38] Fix test --- crates/pop-cli/tests/parachain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 58cfbe441..e07b653bd 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -138,7 +138,7 @@ async fn parachain_lifecycle() -> Result<()> { assert_eq!( contracts_pallet_config_content, r#"use crate::Balances; - + parameter_types! { pub Schedule : pallet_contracts::Schedule < Runtime > = Default::default(); } From 515b2e9a0827b8a729bbcd458ca56fa39fb3be53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:30:30 +0200 Subject: [PATCH 10/38] Fix parachain test --- crates/pop-cli/tests/parachain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index e07b653bd..dc9f79858 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -60,7 +60,7 @@ async fn parachain_lifecycle() -> Result<()> { Command::cargo_bin("pop") .unwrap() .current_dir(&test_parachain) - .args(&["add", "pallet", "-p", "contracts=40.0.1"]) + .args(&["add", "pallet", "-p", "contracts=40.0.0"]) .assert() .success(); @@ -89,7 +89,7 @@ async fn parachain_lifecycle() -> Result<()> { ]; let expected_inserted_lines_configs_mod = vec!["mod contracts;\n"]; let expected_inserted_lines_workspace_manifest = - vec!["pallet-contracts = { version = \"40.0.1\", default-features = false }\n"]; + vec!["pallet-contracts = { version = \"40.0.0\", default-features = false }\n"]; let expected_inserted_lines_runtime_manifest = vec!["pallet-contracts = { workspace = true, default-features = false }\n"]; From 76f624d10667b21ebabf4c620b61f53a138e1a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:44:03 +0200 Subject: [PATCH 11/38] Fix test --- crates/pop-cli/tests/parachain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index dc9f79858..b2deb9f17 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -60,7 +60,7 @@ async fn parachain_lifecycle() -> Result<()> { Command::cargo_bin("pop") .unwrap() .current_dir(&test_parachain) - .args(&["add", "pallet", "-p", "contracts=40.0.0"]) + .args(&["add", "pallet", "-p", "contracts=40.1.0"]) .assert() .success(); @@ -89,7 +89,7 @@ async fn parachain_lifecycle() -> Result<()> { ]; let expected_inserted_lines_configs_mod = vec!["mod contracts;\n"]; let expected_inserted_lines_workspace_manifest = - vec!["pallet-contracts = { version = \"40.0.0\", default-features = false }\n"]; + vec!["pallet-contracts = { version = \"40.1.0\", default-features = false }\n"]; let expected_inserted_lines_runtime_manifest = vec!["pallet-contracts = { workspace = true, default-features = false }\n"]; From 1104a2c53ae783a83e3453868b570eccc41ba11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sat, 12 Apr 2025 19:03:50 +0200 Subject: [PATCH 12/38] fix test parachain --- crates/pop-cli/tests/parachain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index b2deb9f17..fe2e86e40 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -60,7 +60,7 @@ async fn parachain_lifecycle() -> Result<()> { Command::cargo_bin("pop") .unwrap() .current_dir(&test_parachain) - .args(&["add", "pallet", "-p", "contracts=40.1.0"]) + .args(&["add", "pallet", "-p", "contracts=39.1.0"]) .assert() .success(); @@ -89,7 +89,7 @@ async fn parachain_lifecycle() -> Result<()> { ]; let expected_inserted_lines_configs_mod = vec!["mod contracts;\n"]; let expected_inserted_lines_workspace_manifest = - vec!["pallet-contracts = { version = \"40.1.0\", default-features = false }\n"]; + vec!["pallet-contracts = { version = \"39.1.0\", default-features = false }\n"]; let expected_inserted_lines_runtime_manifest = vec!["pallet-contracts = { workspace = true, default-features = false }\n"]; From 5b020dd59d6cc0f55a98d0490a9ec7a5b8abdad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sat, 12 Apr 2025 19:11:43 +0200 Subject: [PATCH 13/38] Parachain test compiles pallet contracts version 39.0.0 --- crates/pop-cli/tests/parachain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index fe2e86e40..fe1087c8d 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -60,7 +60,7 @@ async fn parachain_lifecycle() -> Result<()> { Command::cargo_bin("pop") .unwrap() .current_dir(&test_parachain) - .args(&["add", "pallet", "-p", "contracts=39.1.0"]) + .args(&["add", "pallet", "-p", "contracts=39.0.0"]) .assert() .success(); @@ -89,7 +89,7 @@ async fn parachain_lifecycle() -> Result<()> { ]; let expected_inserted_lines_configs_mod = vec!["mod contracts;\n"]; let expected_inserted_lines_workspace_manifest = - vec!["pallet-contracts = { version = \"39.1.0\", default-features = false }\n"]; + vec!["pallet-contracts = { version = \"39.0.0\", default-features = false }\n"]; let expected_inserted_lines_runtime_manifest = vec!["pallet-contracts = { workspace = true, default-features = false }\n"]; From 7bc09e2a37f2d3ff37f1a451b14f696b369a3323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sun, 13 Apr 2025 12:10:32 +0200 Subject: [PATCH 14/38] pop add pallet adds pallet features to runtime manifest --- Cargo.lock | 3 +- crates/pop-cli/src/commands/add/pallet.rs | 5 + .../src/commands/add/pallet/common_pallets.rs | 4 +- crates/pop-cli/tests/parachain.rs | 14 +- crates/pop-common/Cargo.toml | 1 + crates/pop-common/src/manifest.rs | 215 ++++++++++++++++++ 6 files changed, 233 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 512c2ef92..49ecb1111 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8073,8 +8073,6 @@ dependencies = [ "dirs", "duct", "env_logger 0.11.7", - "frame-benchmarking-cli", - "frame-try-runtime", "fs_rollback", "git2", "mockito", @@ -8126,6 +8124,7 @@ dependencies = [ "scale-info", "serde", "serde_json", + "similar", "sp-core 32.0.0", "strum 0.26.3", "strum_macros 0.26.4", diff --git a/crates/pop-cli/src/commands/add/pallet.rs b/crates/pop-cli/src/commands/add/pallet.rs index 449b82086..d89c76d30 100644 --- a/crates/pop-cli/src/commands/add/pallet.rs +++ b/crates/pop-cli/src/commands/add/pallet.rs @@ -278,6 +278,11 @@ impl AddPalletCommand { false, ), )?; + + pop_common::manifest::add_pallet_features_to_manifest( + roll_runtime_manifest, + pallet.get_crate_name(), + )?; } rollback.commit()?; diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index c21cad238..b9dbf53e8 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -22,8 +22,8 @@ pub(crate) enum CommonPallets { impl CommonPallets { pub(crate) fn get_crate_name(&self) -> String { match self { - CommonPallets::Balances => "pallet-balances".to_string(), - CommonPallets::Contracts => "pallet-contracts".to_string(), + CommonPallets::Balances => "pallet-balances".to_owned(), + CommonPallets::Contracts => "pallet-contracts".to_owned(), } } diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index fe1087c8d..889e94104 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -60,7 +60,7 @@ async fn parachain_lifecycle() -> Result<()> { Command::cargo_bin("pop") .unwrap() .current_dir(&test_parachain) - .args(&["add", "pallet", "-p", "contracts=39.0.0"]) + .args(&["add", "pallet", "-p", "contracts=39.1.0"]) .assert() .success(); @@ -89,9 +89,14 @@ async fn parachain_lifecycle() -> Result<()> { ]; let expected_inserted_lines_configs_mod = vec!["mod contracts;\n"]; let expected_inserted_lines_workspace_manifest = - vec!["pallet-contracts = { version = \"39.0.0\", default-features = false }\n"]; - let expected_inserted_lines_runtime_manifest = - vec!["pallet-contracts = { workspace = true, default-features = false }\n"]; + vec!["pallet-contracts = { version = \"39.1.0\", default-features = false }\n"]; + + let expected_inserted_lines_runtime_manifest = vec![ + "pallet-contracts = { workspace = true, default-features = false }\n", + " \"xcm/std\", \"pallet-contracts/std\",\n", + " \"xcm-executor/runtime-benchmarks\", \"pallet-contracts/runtime-benchmarks\",\n", + " \"sp-runtime/try-runtime\", \"pallet-contracts/try-runtime\",\n", + ]; let mut inserted_lines_runtime_lib = Vec::with_capacity(3); let mut inserted_lines_configs_mod = Vec::with_capacity(1); @@ -124,7 +129,6 @@ async fn parachain_lifecycle() -> Result<()> { for change in runtime_manifest_diff.iter_all_changes() { match change.tag() { - ChangeTag::Delete => panic!("no deletion expected"), ChangeTag::Insert => inserted_lines_runtime_manifest.push(change.value()), _ => (), } diff --git a/crates/pop-common/Cargo.toml b/crates/pop-common/Cargo.toml index f2f1b0bfa..b9a82a3cd 100644 --- a/crates/pop-common/Cargo.toml +++ b/crates/pop-common/Cargo.toml @@ -38,3 +38,4 @@ url.workspace = true [dev-dependencies] mockito.workspace = true tempfile.workspace = true +similar = "2.7.0" diff --git a/crates/pop-common/src/manifest.rs b/crates/pop-common/src/manifest.rs index a45dc10a1..954c5f0e3 100644 --- a/crates/pop-common/src/manifest.rs +++ b/crates/pop-common/src/manifest.rs @@ -157,9 +157,67 @@ pub fn add_feature(project: &Path, (key, items): (String, Vec)) -> anyho Ok(()) } +/// Add pallet features (std, runtime-benchmarks, try-runtime) to manifest. +pub fn add_pallet_features_to_manifest>( + manifest_path: P, + pallet_crate_name: String, +) -> anyhow::Result<()> { + fn do_add_pallet_features_to_manifest( + manifest_path: &Path, + pallet_crate_name: String, + ) -> anyhow::Result<()> { + let cargo_toml_content = std::fs::read_to_string(manifest_path)?; + let mut doc = cargo_toml_content.parse::()?; + + if !doc.as_table().contains_key("features") { + return Err(anyhow::anyhow!( + "The runtime manifest does not contain a [features] section" + )); + } + + let features_to_add = vec![ + ("std", format!("{}/std", pallet_crate_name)), + ("runtime-benchmarks", format!("{}/runtime-benchmarks", pallet_crate_name)), + ("try-runtime", format!("{}/try-runtime", pallet_crate_name)), + ]; + + let features_table = + doc.get_mut("features").and_then(|item| item.as_table_mut()).ok_or_else(|| { + anyhow::anyhow!("The runtime manifest does not contain a valid [features] table") + })?; + + for (feature, dep_feature) in features_to_add { + let feature_item = features_table.get_mut(feature).ok_or_else(|| { + anyhow::anyhow!(format!( + "Feature `{}` does not exist in the runtime manifest", + feature + )) + })?; + + if feature_item.is_array() { + let array = feature_item.as_array_mut().unwrap(); + if !array.iter().any(|v| v.as_str() == Some(&dep_feature)) { + array.push(dep_feature); + } + } else { + return Err(anyhow::anyhow!(format!( + "Feature `{}` is not an array in the runtime manifest", + feature + ))); + } + } + + std::fs::write(manifest_path, doc.to_string())?; + Ok(()) + } + + do_add_pallet_features_to_manifest(manifest_path.as_ref(), pallet_crate_name) +} + #[cfg(test)] mod tests { use super::*; + use similar::{ChangeTag, TextDiff}; use std::fs::{write, File}; use tempfile::TempDir; @@ -561,4 +619,161 @@ mod tests { read_to_string(&cargo_toml_path).expect("Cargo.toml should be readable"); assert_eq!(initial_toml_content, final_toml_content); } + + #[test] + fn add_pallet_features_to_manifest_works() { + let test_builder = TestBuilder::default().add_workspace().add_workspace_cargo_toml( + r#"[workspace] +resolver = "2" +members = ["member1"] + +[features] +default = ["std"] + +std = [ +"pallet-balances/std", +"pallet-aura/std" +] + +runtime-benchmarks = [ +"pallet-balances/runtime-benchmarks", +"pallet-aura/runtime-benchmarks" +] + +try-runtime = [ +"pallet-balances/try-runtime", +"pallet-aura/try-runtime" +] +"#, + ); + + let manifest_path = test_builder.workspace_cargo_toml.clone().unwrap(); + + let manifest_content_before = std::fs::read_to_string(&manifest_path).unwrap(); + + assert!( + add_pallet_features_to_manifest(&manifest_path, "pallet-contracts".to_owned()).is_ok() + ); + + let manifest_content_after = std::fs::read_to_string(&manifest_path).unwrap(); + + let manifest_diff = TextDiff::from_lines(&manifest_content_before, &manifest_content_after); + + let expected_inserted_lines = vec![ + ", \"pallet-contracts/std\"]\n", + ", \"pallet-contracts/runtime-benchmarks\"]\n", + ", \"pallet-contracts/try-runtime\"]\n", + ]; + + let mut inserted_lines = Vec::with_capacity(3); + + for change in manifest_diff.iter_all_changes() { + match change.tag() { + ChangeTag::Insert => inserted_lines.push(change.value()), + _ => (), + } + } + + assert_eq!(inserted_lines, expected_inserted_lines); + } + + #[test] + fn add_pallet_features_to_manifest_missing_features_section_should_fail() { + let test_builder = TestBuilder::default().add_workspace().add_workspace_cargo_toml( + r#"[workspace] +resolver = "2" +members = ["member1"] + +[package] +name = "dummy" +version = "0.1.0" +"#, + ); + let manifest_path = test_builder.workspace_cargo_toml.clone().unwrap(); + + let res = add_pallet_features_to_manifest(&manifest_path, "pallet-contracts".to_owned()); + assert!( + res.is_err(), + "Expected error because the manifest does not contain a [features] section" + ); + let err_msg = format!("{}", res.unwrap_err()); + assert!( + err_msg.contains("does not contain a [features] section"), + "Error message did not contain the expected text, got: {}", + err_msg + ); + } + + #[test] + fn add_pallet_features_to_manifest_missing_feature_key_should_fail() { + // Here, we purposefully omit the "std" feature. + let test_builder = TestBuilder::default().add_workspace().add_workspace_cargo_toml( + r#"[workspace] +resolver = "2" +members = ["member1"] + +[features] +default = ["std"] + +runtime-benchmarks = [ + "pallet-balances/runtime-benchmarks", + "pallet-aura/runtime-benchmarks" +] + +try-runtime = [ + "pallet-balances/try-runtime", + "pallet-aura/try-runtime" +] +"#, + ); + let manifest_path = test_builder.workspace_cargo_toml.clone().unwrap(); + + let res = add_pallet_features_to_manifest(&manifest_path, "pallet-contracts".to_owned()); + assert!( + res.is_err(), + "Expected error because the 'std' feature key is missing in the manifest" + ); + let err_msg = format!("{}", res.unwrap_err()); + assert!( + err_msg.contains("Feature `std` does not exist"), + "Error message did not contain the expected text, got: {}", + err_msg + ); + } + + #[test] + fn add_pallet_features_to_manifest_non_array_feature_should_fail() { + // Here, the "std" feature is defined as a string instead of an array. + let test_builder = TestBuilder::default().add_workspace().add_workspace_cargo_toml( + r#"[workspace] +resolver = "2" +members = ["member1"] + +[features] +default = ["std"] + +std = "pallet-balances/std" + +runtime-benchmarks = [ + "pallet-balances/runtime-benchmarks", + "pallet-aura/runtime-benchmarks" +] + +try-runtime = [ + "pallet-balances/try-runtime", + "pallet-aura/try-runtime" +] +"#, + ); + let manifest_path = test_builder.workspace_cargo_toml.clone().unwrap(); + + let res = add_pallet_features_to_manifest(&manifest_path, "pallet-contracts".to_owned()); + assert!(res.is_err(), "Expected error because the 'std' feature is not an array"); + let err_msg = format!("{}", res.unwrap_err()); + assert!( + err_msg.contains("Feature `std` is not an array"), + "Error message did not contain the expected text, got: {}", + err_msg + ); + } } From d1d858863b515651e8ff99434991bf4c126398e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sun, 13 Apr 2025 12:57:32 +0200 Subject: [PATCH 15/38] Adding missing imports --- .../src/commands/add/pallet/common_pallets.rs | 74 ++++++++++++------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index b9dbf53e8..eeea9ad34 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -51,14 +51,24 @@ impl CommonPallets { pub(crate) fn get_impl_needed_use_statements(&self) -> Vec { match self { - CommonPallets::Balances => vec![parse_quote!( - ///TEMP_DOC - use crate::System; - )], - CommonPallets::Contracts => vec![parse_quote!( - ///TEMP_DOC - use crate::Balances; - )], + CommonPallets::Balances => vec![ + parse_quote!( + ///TEMP_DOC + use crate::{System, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + ), + parse_quote!( + use frame_support::{parameter_types, derive_impl}; + ), + ], + CommonPallets::Contracts => vec![ + parse_quote!( + ///TEMP_DOC + use crate::{System, Runtime, Balances. RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + ), + parse_quote!( + use frame_support::{parameter_types, derive_impl}; + ), + ], } } @@ -68,7 +78,7 @@ impl CommonPallets { CommonPallets::Contracts => parse_quote! { ///TEMP_DOC parameter_types!{ - pub Schedule: pallet_contracts::Schedule = Default::default(); + pub Schedule: pallet_contracts::Schedule = >::default(); } }, } @@ -88,8 +98,8 @@ impl CommonPallets { #[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] impl pallet_contracts::Config for Runtime{ type Currency = Balances; - type Schedule = [pallet_contracts::Frame; 5]; - type CallStack = Schedule; + type Schedule = Schedule; + type CallStack = [pallet_contracts::Frame; 5]; } }, } @@ -209,18 +219,28 @@ mod tests { #[test] fn get_impl_needed_use_statements_works() { assert_eq!( - CommonPallets::Balances.get_impl_needed_use_statements()[0], - parse_quote! { - ///TEMP_DOC - use crate::System; - } + CommonPallets::Balances.get_impl_needed_use_statements(), + vec![ + parse_quote! { + ///TEMP_DOC + use crate::{System, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + }, + parse_quote!( + use frame_support::{parameter_types, derive_impl}; + ) + ] ); assert_eq!( - CommonPallets::Contracts.get_impl_needed_use_statements()[0], - parse_quote! { - ///TEMP_DOC - use crate::Balances; - } + CommonPallets::Contracts.get_impl_needed_use_statements(), + vec![ + parse_quote! { + ///TEMP_DOC + use crate::{System, Runtime, Balances. RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + }, + parse_quote!( + use frame_support::{parameter_types, derive_impl}; + ) + ] ); } @@ -236,7 +256,7 @@ mod tests { parse_quote! { ///TEMP_DOC parameter_types!{ - pub Schedule: pallet_contracts::Schedule = Default::default(); + pub Schedule: pallet_contracts::Schedule = >::default(); } } ); @@ -259,11 +279,11 @@ mod tests { CommonPallets::Contracts.get_needed_impl_block(), parse_quote! { ///TEMP_DOC - #[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] - impl pallet_contracts::Config for Runtime { - type Currency = Balances; - type Schedule = [pallet_contracts::Frame; 5]; - type CallStack = Schedule; + #[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] + impl pallet_contracts::Config for Runtime{ + type Currency = Balances; + type Schedule = Schedule; + type CallStack = [pallet_contracts::Frame; 5]; } } ); From 54b0a5e98a62dfa5bfa59046f054c24d88eb0912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sun, 13 Apr 2025 13:07:34 +0200 Subject: [PATCH 16/38] Fix typo --- crates/pop-cli/src/commands/add/pallet/common_pallets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index eeea9ad34..51f386642 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -63,7 +63,7 @@ impl CommonPallets { CommonPallets::Contracts => vec![ parse_quote!( ///TEMP_DOC - use crate::{System, Runtime, Balances. RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + use crate::{System, Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; ), parse_quote!( use frame_support::{parameter_types, derive_impl}; From 1be64fa8dac3d09e60e6e6a9b7b780e7926388c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sun, 13 Apr 2025 13:10:03 +0200 Subject: [PATCH 17/38] fmt --- crates/pop-cli/src/commands/add/pallet/common_pallets.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 51f386642..1629f311e 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -63,7 +63,9 @@ impl CommonPallets { CommonPallets::Contracts => vec![ parse_quote!( ///TEMP_DOC - use crate::{System, Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + use crate::{ + System, Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall + }; ), parse_quote!( use frame_support::{parameter_types, derive_impl}; From f1eb03dcf43e916a74dfc18fdce8db8a973b831e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sun, 13 Apr 2025 13:11:43 +0200 Subject: [PATCH 18/38] Fmt --- crates/pop-cli/src/commands/add/pallet/common_pallets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 1629f311e..da47b4f36 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -64,7 +64,7 @@ impl CommonPallets { parse_quote!( ///TEMP_DOC use crate::{ - System, Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall + System, Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall, }; ), parse_quote!( From 995435cc09044bb63cee3be66f1561e0e7780191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sun, 13 Apr 2025 13:25:34 +0200 Subject: [PATCH 19/38] Fix test --- crates/pop-cli/tests/parachain.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 889e94104..3eaed76c6 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -141,19 +141,21 @@ async fn parachain_lifecycle() -> Result<()> { assert_eq!( contracts_pallet_config_content, - r#"use crate::Balances; - -parameter_types! { - pub Schedule : pallet_contracts::Schedule < Runtime > = Default::default(); -} - -#[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] -impl pallet_contracts::Config for Runtime { - type Currency = Balances; - type Schedule = [pallet_contracts::Frame; 5]; - type CallStack = Schedule; -} -"# + r#"use crate::{Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeHoldReason, System}; + use frame_support::{derive_impl, parameter_types}; + + parameter_types! { + pub Schedule : pallet_contracts::Schedule < Runtime > = < pallet_contracts::Schedule + < Runtime >> ::default(); + } + + #[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] + impl pallet_contracts::Config for Runtime { + type Currency = Balances; + type Schedule = Schedule; + type CallStack = [pallet_contracts::Frame; 5]; + } + "# ); // pop build --release --path "./test_parachain" From a2c36d96c3c305a488da309693fa8f8d679d69f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Sun, 13 Apr 2025 13:31:34 +0200 Subject: [PATCH 20/38] Fix test --- crates/pop-cli/tests/parachain.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 3eaed76c6..7e2f6af89 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -142,20 +142,20 @@ async fn parachain_lifecycle() -> Result<()> { assert_eq!( contracts_pallet_config_content, r#"use crate::{Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeHoldReason, System}; - use frame_support::{derive_impl, parameter_types}; +use frame_support::{derive_impl, parameter_types}; - parameter_types! { - pub Schedule : pallet_contracts::Schedule < Runtime > = < pallet_contracts::Schedule - < Runtime >> ::default(); - } +parameter_types! { + pub Schedule : pallet_contracts::Schedule < Runtime > = < pallet_contracts::Schedule + < Runtime >> ::default(); +} - #[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] - impl pallet_contracts::Config for Runtime { - type Currency = Balances; - type Schedule = Schedule; - type CallStack = [pallet_contracts::Frame; 5]; - } - "# +#[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] +impl pallet_contracts::Config for Runtime { + type Currency = Balances; + type Schedule = Schedule; + type CallStack = [pallet_contracts::Frame; 5]; +} +"# ); // pop build --release --path "./test_parachain" From 63661ec3034bbcb0781d39c3fbfef35474861f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sun, 13 Apr 2025 15:22:47 +0200 Subject: [PATCH 21/38] Fix tests --- .../src/commands/add/pallet/common_pallets.rs | 2 +- crates/pop-cli/tests/parachain.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index da47b4f36..098533f5a 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -237,7 +237,7 @@ mod tests { vec![ parse_quote! { ///TEMP_DOC - use crate::{System, Runtime, Balances. RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + use crate::{System, Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; }, parse_quote!( use frame_support::{parameter_types, derive_impl}; diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 7e2f6af89..7c32f63b4 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -143,17 +143,17 @@ async fn parachain_lifecycle() -> Result<()> { contracts_pallet_config_content, r#"use crate::{Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeHoldReason, System}; use frame_support::{derive_impl, parameter_types}; - + parameter_types! { - pub Schedule : pallet_contracts::Schedule < Runtime > = < pallet_contracts::Schedule - < Runtime >> ::default(); + pub Schedule : pallet_contracts::Schedule < Runtime > = < pallet_contracts::Schedule + < Runtime >> ::default(); } - + #[derive_impl(pallet_contracts::config_preludes::TestDefaultConfig)] impl pallet_contracts::Config for Runtime { - type Currency = Balances; - type Schedule = Schedule; - type CallStack = [pallet_contracts::Frame; 5]; + type Currency = Balances; + type Schedule = Schedule; + type CallStack = [pallet_contracts::Frame; 5]; } "# ); From 0c8944856e5568349e31d4cf1f2b66e63da144cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Sun, 13 Apr 2025 18:20:50 +0200 Subject: [PATCH 22/38] Fixing version issue compatibility in parachain test --- crates/pop-cli/src/commands/add/pallet/common_pallets.rs | 6 ++---- crates/pop-cli/tests/parachain.rs | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 098533f5a..38af166cc 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -63,9 +63,7 @@ impl CommonPallets { CommonPallets::Contracts => vec![ parse_quote!( ///TEMP_DOC - use crate::{ - System, Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall, - }; + use crate::{Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; ), parse_quote!( use frame_support::{parameter_types, derive_impl}; @@ -237,7 +235,7 @@ mod tests { vec![ parse_quote! { ///TEMP_DOC - use crate::{System, Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + use crate::{Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; }, parse_quote!( use frame_support::{parameter_types, derive_impl}; diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 7c32f63b4..1e4d3fce4 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -60,7 +60,7 @@ async fn parachain_lifecycle() -> Result<()> { Command::cargo_bin("pop") .unwrap() .current_dir(&test_parachain) - .args(&["add", "pallet", "-p", "contracts=39.1.0"]) + .args(&["add", "pallet", "-p", "contracts=39.0.0"]) .assert() .success(); @@ -89,7 +89,7 @@ async fn parachain_lifecycle() -> Result<()> { ]; let expected_inserted_lines_configs_mod = vec!["mod contracts;\n"]; let expected_inserted_lines_workspace_manifest = - vec!["pallet-contracts = { version = \"39.1.0\", default-features = false }\n"]; + vec!["pallet-contracts = { version = \"39.0.0\", default-features = false }\n"]; let expected_inserted_lines_runtime_manifest = vec![ "pallet-contracts = { workspace = true, default-features = false }\n", @@ -141,7 +141,7 @@ async fn parachain_lifecycle() -> Result<()> { assert_eq!( contracts_pallet_config_content, - r#"use crate::{Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeHoldReason, System}; + r#"use crate::{Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeHoldReason}; use frame_support::{derive_impl, parameter_types}; parameter_types! { From a9bbec1e91d5060107548fc1ffa51027e76eb32f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:18:04 +0200 Subject: [PATCH 23/38] Update crates/pop-cli/tests/parachain.rs Co-authored-by: Alex Bean --- crates/pop-cli/tests/parachain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 1e4d3fce4..b0ba0f9b1 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -9,7 +9,7 @@ use std::{fs, path::Path, process::Command as Cmd}; use strum::VariantArray; use tokio::time::{sleep, Duration}; -/// Test the parachain lifecycle: new, add,build, up, call. +/// Test the parachain lifecycle: new, add pallet ,build, up, call. #[tokio::test] async fn parachain_lifecycle() -> Result<()> { let temp = tempfile::tempdir().unwrap(); From be747578de114fcbaa3ef9f5b38d3a72f53a26ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:33:33 +0200 Subject: [PATCH 24/38] Update crates/pop-common/src/manifest.rs Co-authored-by: Alex Bean --- crates/pop-common/src/manifest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-common/src/manifest.rs b/crates/pop-common/src/manifest.rs index 954c5f0e3..67428220a 100644 --- a/crates/pop-common/src/manifest.rs +++ b/crates/pop-common/src/manifest.rs @@ -195,7 +195,7 @@ pub fn add_pallet_features_to_manifest>( })?; if feature_item.is_array() { - let array = feature_item.as_array_mut().unwrap(); + let array = feature_item.as_array_mut()?; if !array.iter().any(|v| v.as_str() == Some(&dep_feature)) { array.push(dep_feature); } From 2656010b7ff9f5056e7814cf11f434ca180eba48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:44:06 +0200 Subject: [PATCH 25/38] Update crates/pop-cli/src/commands/add/pallet/common_pallets.rs Co-authored-by: Alex Bean --- crates/pop-cli/src/commands/add/pallet/common_pallets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 38af166cc..e822b6a3c 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -139,7 +139,7 @@ impl FromStr for InputPallet { // Not interested in using the Version type at all, just need to know if this &str can be // parsed as Version - let _ = parts[1] + parts[1] .parse::() .map_err(|e| format!("Invalid version '{}': {}", parts[1], e))?; From 8228181c4706b581b361e1f5be61dc89ca8de1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:45:54 +0200 Subject: [PATCH 26/38] Update crates/pop-cli/src/commands/add/pallet/common_pallets.rs Co-authored-by: Alex Bean --- crates/pop-cli/src/commands/add/pallet/common_pallets.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index e822b6a3c..88d90c1d2 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -174,7 +174,6 @@ impl TypedValueParser for InputPalletParser { #[cfg(test)] mod tests { - use super::*; #[test] From ec83aafbc85d3a089f35f2177d60b088f273dc4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:46:27 +0200 Subject: [PATCH 27/38] Update crates/pop-cli/src/commands/add/pallet/common_pallets.rs Co-authored-by: Alex Bean --- crates/pop-cli/src/commands/add/pallet/common_pallets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 88d90c1d2..529c8fe49 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -167,7 +167,7 @@ impl TypedValueParser for InputPalletParser { fn possible_values(&self) -> Option + '_>> { let iter = CommonPallets::value_variants() .iter() - .map(|variant| variant.to_possible_value().expect("value should be possible")); + .map(|variant| variant.to_possible_value()?); Some(Box::new(iter)) } } From 3f002e9004faf3dfcafa56f1148d6a741eb61560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Tue, 29 Apr 2025 19:25:11 +0200 Subject: [PATCH 28/38] Addressing Alex's suggestions --- Cargo.lock | 1 - Cargo.toml | 2 +- crates/pop-cli/Cargo.toml | 3 +- crates/pop-cli/src/commands/add/pallet.rs | 374 +++++++++--------- .../src/commands/add/pallet/common_pallets.rs | 134 +------ crates/pop-cli/src/commands/mod.rs | 4 + crates/pop-cli/src/common/writer.rs | 125 +++--- crates/pop-cli/tests/parachain.rs | 25 +- crates/pop-common/Cargo.toml | 2 +- crates/pop-common/src/errors.rs | 3 + crates/pop-common/src/manifest.rs | 20 +- 11 files changed, 290 insertions(+), 403 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49ecb1111..b2c2161f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8086,7 +8086,6 @@ dependencies = [ "reqwest", "rust_writer", "rustilities", - "semver", "serde", "serde_json", "similar", diff --git a/Cargo.toml b/Cargo.toml index 211632f56..fac55d8a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ mockito = { version = "1.4.0", default-features = false } proc-macro2 = "1.0.86" rustilities = "2.2.1" rust_writer = "1.0.4" -semver = "1.0.26" +similar = "2.7.0" syn = "2.0.100" tar = { version = "0.4.40", default-features = false } tempfile = { version = "3.10", default-features = false } diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index 649d6126e..5eaf0054c 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -24,7 +24,6 @@ fs_rollback.workspace = true os_info.workspace = true proc-macro2.workspace = true reqwest.workspace = true -semver.workspace = true rustilities = { workspace = true, features = ["fmt", "manifest"]} rust_writer.workspace = true serde = { workspace = true, features = ["derive"] } @@ -66,7 +65,7 @@ rustilities = { workspace = true, features = ["fmt", "manifest", "parsing"]} subxt.workspace = true subxt-signer.workspace = true sp-weights.workspace = true -similar = "2.7.0" +similar.workspace = true [features] default = ["contract", "parachain", "telemetry"] diff --git a/crates/pop-cli/src/commands/add/pallet.rs b/crates/pop-cli/src/commands/add/pallet.rs index d89c76d30..9b7e0ecad 100644 --- a/crates/pop-cli/src/commands/add/pallet.rs +++ b/crates/pop-cli/src/commands/add/pallet.rs @@ -2,10 +2,10 @@ use crate::{ cli::{traits::Cli as _, Cli}, - common::writer::{self, RuntimeUsedMacro}, + common::writer::{self, PalletConfigRelatedPaths, RuntimeUsedMacro}, }; use clap::{error::ErrorKind, Args, Command}; -use common_pallets::{InputPallet, InputPalletParser}; +use common_pallets::CommonPallets; use fs_rollback::Rollback; use rust_writer::{ ast::{ @@ -19,7 +19,7 @@ use rust_writer::{ }; use rustilities::manifest::{ManifestDependencyConfig, ManifestDependencyOrigin}; use std::{env, path::PathBuf}; -use strum::EnumMessage; +use strum::{EnumMessage, IntoEnumIterator}; use syn::{parse_quote, visit_mut::VisitMut}; mod common_pallets; @@ -31,15 +31,8 @@ struct PalletImplBlockImplementor; #[derive(Args, Debug, Clone)] pub struct AddPalletCommand { - #[arg( - long, - short, - num_args(1..), - required = true, - value_parser = InputPalletParser, - help = "The pallets added to the runtime. The input should follow the format =, where is one of the options described below." - )] - pub(crate) pallets: Vec, + #[arg(long, short, help = "The pallet added to the runtime.")] + pub(crate) pallet: Option, #[arg( short, long, @@ -51,6 +44,8 @@ pub struct AddPalletCommand { help = "pop add pallet will place the impl blocks for your pallets' Config traits inside a dedicated file under the configs directory. Use this argument to place them somewhere else." )] pub(crate) pallet_impl_path: Option, + #[arg(long, short, help = "The pallet version.")] + pub(crate) version: Option, } const INVALID_DIR_MSG: &str = "Make sure to run this command either in a runtime crate contained in a workspace, in the workspace itself or to specify the path to the runtime crate using -r."; @@ -60,6 +55,34 @@ impl AddPalletCommand { Cli.intro("Add a new pallet to your runtime")?; let mut cmd = Command::new(""); + let (pallet, version) = match (self.pallet, self.version) { + (Some(pallet), Some(version)) => (pallet, version), + (None, None) => { + let mut pallet_prompt = + cliclack::select("Select a pallet to add to your runtime: ".to_owned()); + for (i, pallet) in CommonPallets::iter().enumerate() { + if i == 0 { + pallet_prompt = pallet_prompt.initial_value(pallet); + } + pallet_prompt = pallet_prompt.item( + pallet, + pallet.get_message().expect("all variants of CommonPallets have message; qed;"), + pallet.get_detailed_message().expect("all variants of CommonPallets have detailed_message; qed;"), + ); + } + let mut version_prompt = cliclack::input("Which version should use your pallet?") + .placeholder("1.0.0") + .default_input("1.0.0"); + (pallet_prompt.interact()?, version_prompt.interact()?) + }, + _ => cmd + .error( + ErrorKind::Io, + "If you specify pallet/version via the command line, both fields must be specified" + ) + .exit(), + }; + let runtime_path = if let Some(path) = &self.runtime_path { pop_common::helpers::prefix_with_current_dir_if_needed(&path) } else { @@ -76,8 +99,17 @@ impl AddPalletCommand { } }; - let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = - writer::compute_pallet_related_paths(&runtime_path); + let spinner = cliclack::spinner(); + spinner.start("Updating runtime..."); + + let pallet_name = pallet + .get_message() + .expect("All pallets in common_pallets::CommonPallets have a defined message; qed;"); + + let pallet_config_related_paths = writer::compute_pallet_related_paths(&runtime_path); + + let PalletConfigRelatedPaths { runtime_lib_path, configs_folder_path, .. } = + pallet_config_related_paths.clone(); let runtime_lib_content = std::fs::read_to_string(&runtime_lib_path)?; @@ -87,23 +119,10 @@ impl AddPalletCommand { cmd.error(ErrorKind::InvalidValue, INVALID_DIR_MSG).exit(); } - let spinner = cliclack::spinner(); - spinner.start("Updating runtime..."); + let pallet_config_path = configs_folder_path.join(format!("{}.rs", pallet_name)); let mut rollback = Rollback::default(); - let mut precomputed_pallet_config_paths = Vec::with_capacity(self.pallets.len()); - - for pallet in self.pallets.iter() { - let InputPallet { pallet, .. } = pallet; - - let pallet_name = pallet.get_message().expect( - "All pallets in common_pallets::CommonPallets have a defined message; qed;", - ); - precomputed_pallet_config_paths - .push(configs_folder_path.join(format!("{}.rs", pallet_name))); - } - let runtime_manifest = rustilities::manifest::find_innermost_manifest(&runtime_path) .ok_or(anyhow::anyhow!(INVALID_DIR_MSG))?; @@ -111,7 +130,9 @@ impl AddPalletCommand { .ok_or(anyhow::anyhow!(INVALID_DIR_MSG))?; rollback.note_file(&runtime_lib_path)?; + rollback.note_file(&runtime_manifest)?; + rollback.note_file(&workspace_manifest)?; if let Some(ref pallet_impl_path) = self.pallet_impl_path { @@ -123,168 +144,153 @@ impl AddPalletCommand { } } - for (index, pallet) in self.pallets.iter().enumerate() { - let InputPallet { pallet, version } = pallet; - - let pallet_name = pallet.get_message().expect( - "All pallets in common_pallets::CommonPallets have a defined message; qed;", - ); - - let pallet_config_path = &precomputed_pallet_config_paths[index]; - - let roll_pallet_impl_path = match self.pallet_impl_path { - Some(ref pallet_impl_path) => rollback - .get_noted_file(pallet_impl_path) - .expect("The file has been noted above;qed;"), - None => { - rollback = writer::create_new_pallet_impl_path_structure( - rollback, - &runtime_lib_path, - &configs_rs_path, - &configs_folder_path, - &configs_mod_path, - &pallet_config_path, - &pallet_name, - )?; - - rollback - .get_new_file(&pallet_config_path) - .expect("create_new_pallet_impl_path_structure noted this file; qed;") - }, - }; - - let roll_runtime_lib_path = rollback - .get_noted_file(&runtime_lib_path) - .expect("The file has been noted above; qed;"); - - let roll_runtime_manifest = rollback - .get_noted_file(&runtime_manifest) - .expect("The file has been noted above; qed;"); - - let roll_workspace_manifest = rollback - .get_noted_file(&workspace_manifest) - .expect("The file has been noted above; qed;"); - - // Add the pallet to the runtime module - let construct_runtime_preserver = Preserver::new("construct_runtime!"); - let mod_runtime_preserver = Preserver::new("mod runtime"); - let mut preserved_ast = rust_writer::preserver::preserve_and_parse( - roll_runtime_lib_path, - &[&construct_runtime_preserver, &mod_runtime_preserver], - )?; - - // Parse the runtime to find which of the runtime macros is being used and the highest - // pallet index used (if needed, otherwise 0). - let used_macro = writer::find_used_runtime_macro(&preserved_ast)?; - match used_macro { - RuntimeUsedMacro::Runtime => { - let highest_index = writer::find_highest_pallet_index(&preserved_ast)?; - let pallet_to_runtime_implementor: ItemToMod = - ("runtime", pallet.get_pallet_declaration_runtime_module(highest_index)) - .into(); - - let mut finder = Finder::default().to_find(&pallet_to_runtime_implementor); - if finder.find(&preserved_ast) { - return Err(anyhow::anyhow!(format!( - "{} is already in use.", - pallet.get_crate_name() - ))); - } else { - let mut mutator = - Mutator::default().to_mutate(&pallet_to_runtime_implementor); - mutator.mutate(&mut preserved_ast)?; - rust_writer::preserver::resolve_preserved( - &preserved_ast, - roll_runtime_lib_path, - )?; - } - }, - RuntimeUsedMacro::ConstructRuntime => { - let pallet_to_construct_runtime_implementor: TokenStreamToMacro = ( - parse_quote!(construct_runtime), - Some(parse_quote!(Runtime)), - pallet.get_pallet_declaration_construct_runtime(), - ) - .into(); - let mut finder = - Finder::default().to_find(&pallet_to_construct_runtime_implementor); - if finder.find(&preserved_ast) { - return Err(anyhow::anyhow!(format!( - "{} is already in use.", - pallet.get_crate_name() - ))); - } else { - let mut mutator = - Mutator::default().to_mutate(&pallet_to_construct_runtime_implementor); - mutator.mutate(&mut preserved_ast)?; - rust_writer::preserver::resolve_preserved( - &preserved_ast, - roll_runtime_lib_path, - )?; - } - }, - } - - // Add the pallet impl block and its related use statements - let use_preserver = Preserver::new("use"); - let pub_use_preserver = Preserver::new("pub use"); - - let mut preserved_ast = rust_writer::preserver::preserve_and_parse( - roll_pallet_impl_path, - &[&use_preserver, &pub_use_preserver], - )?; + let roll_pallet_impl_path = match self.pallet_impl_path { + Some(ref pallet_impl_path) => rollback + .get_noted_file(pallet_impl_path) + .expect("The file has been noted above;qed;"), + None => { + rollback = writer::create_new_pallet_impl_path_structure( + rollback, + &pallet_config_related_paths, + &pallet_config_path, + &pallet_name, + )?; + + rollback + .get_new_file(&pallet_config_path) + .expect("create_new_pallet_impl_path_structure noted this file; qed;") + }, + }; - for use_statement in pallet.get_impl_needed_use_statements() { - let use_statement: ItemToFile = use_statement.into(); - let mut finder = Finder::default().to_find(&use_statement); - if !finder.find(&preserved_ast) { - let mut mutator = Mutator::default().to_mutate(&use_statement); + let roll_runtime_lib_path = rollback + .get_noted_file(&runtime_lib_path) + .expect("The file has been noted above; qed;"); + + let roll_runtime_manifest = rollback + .get_noted_file(&runtime_manifest) + .expect("The file has been noted above; qed;"); + + let roll_workspace_manifest = rollback + .get_noted_file(&workspace_manifest) + .expect("The file has been noted above; qed;"); + + // Add the pallet to the runtime module + let construct_runtime_preserver = Preserver::new("construct_runtime!"); + let mod_runtime_preserver = Preserver::new("mod runtime"); + let mut preserved_ast = rust_writer::preserver::preserve_and_parse( + roll_runtime_lib_path, + &[&construct_runtime_preserver, &mod_runtime_preserver], + )?; + + // Parse the runtime to find which of the runtime macros is being used and the highest + // pallet index used (if needed, otherwise 0). + let used_macro = writer::find_used_runtime_macro(&preserved_ast)?; + match used_macro { + RuntimeUsedMacro::Runtime => { + let highest_index = writer::find_highest_pallet_index(&preserved_ast)?; + let pallet_to_runtime_implementor: ItemToMod = + ("runtime", pallet.get_pallet_declaration_runtime_module(highest_index)).into(); + + let mut finder = Finder::default().to_find(&pallet_to_runtime_implementor); + if finder.find(&preserved_ast) { + return Err(anyhow::anyhow!(format!( + "{} is already in use.", + pallet.get_crate_name() + ))); + } else { + let mut mutator = Mutator::default().to_mutate(&pallet_to_runtime_implementor); mutator.mutate(&mut preserved_ast)?; + rust_writer::preserver::resolve_preserved( + &preserved_ast, + roll_runtime_lib_path, + )?; } - } + }, + RuntimeUsedMacro::ConstructRuntime => { + let pallet_to_construct_runtime_implementor: TokenStreamToMacro = ( + parse_quote!(construct_runtime), + Some(parse_quote!(Runtime)), + pallet.get_pallet_declaration_construct_runtime(), + ) + .into(); + let mut finder = + Finder::default().to_find(&pallet_to_construct_runtime_implementor); + if finder.find(&preserved_ast) { + return Err(anyhow::anyhow!(format!( + "{} is already in use.", + pallet.get_crate_name() + ))); + } else { + let mut mutator = + Mutator::default().to_mutate(&pallet_to_construct_runtime_implementor); + mutator.mutate(&mut preserved_ast)?; + rust_writer::preserver::resolve_preserved( + &preserved_ast, + roll_runtime_lib_path, + )?; + } + }, + } - let pallet_impl_block_implementor: PalletImplBlockImplementor = ( - ItemToFile { item: pallet.get_needed_parameter_types() }, - ItemToFile { item: pallet.get_needed_impl_block() }, - ) - .into(); - - let mut mutator: PalletImplBlockImplementorMutatorWrapper = - Mutator::default().to_mutate(&pallet_impl_block_implementor).into(); - - mutator.mutate(&mut preserved_ast, None)?; - - rust_writer::preserver::resolve_preserved(&preserved_ast, roll_pallet_impl_path)?; - - // Update the manifests to add the pallet crate - rustilities::manifest::add_crate_to_dependencies( - roll_workspace_manifest, - &pallet.get_crate_name(), - ManifestDependencyConfig::new( - ManifestDependencyOrigin::crates_io(&version), - false, - vec![], - false, - ), - )?; - - rustilities::manifest::add_crate_to_dependencies( - roll_runtime_manifest, - &pallet.get_crate_name(), - ManifestDependencyConfig::new( - ManifestDependencyOrigin::workspace(), - false, - vec![], - false, - ), - )?; - - pop_common::manifest::add_pallet_features_to_manifest( - roll_runtime_manifest, - pallet.get_crate_name(), - )?; + // Add the pallet impl block and its related use statements + let use_preserver = Preserver::new("use"); + let pub_use_preserver = Preserver::new("pub use"); + + let mut preserved_ast = rust_writer::preserver::preserve_and_parse( + roll_pallet_impl_path, + &[&use_preserver, &pub_use_preserver], + )?; + + for use_statement in pallet.get_impl_needed_use_statements() { + let use_statement: ItemToFile = use_statement.into(); + let mut finder = Finder::default().to_find(&use_statement); + if !finder.find(&preserved_ast) { + let mut mutator = Mutator::default().to_mutate(&use_statement); + mutator.mutate(&mut preserved_ast)?; + } } + let pallet_impl_block_implementor: PalletImplBlockImplementor = ( + ItemToFile { item: pallet.get_needed_parameter_types() }, + ItemToFile { item: pallet.get_needed_impl_block() }, + ) + .into(); + + let mut mutator: PalletImplBlockImplementorMutatorWrapper = + Mutator::default().to_mutate(&pallet_impl_block_implementor).into(); + + mutator.mutate(&mut preserved_ast, None)?; + + rust_writer::preserver::resolve_preserved(&preserved_ast, roll_pallet_impl_path)?; + + // Update the manifests to add the pallet crate + rustilities::manifest::add_crate_to_dependencies( + roll_workspace_manifest, + &pallet.get_crate_name(), + ManifestDependencyConfig::new( + ManifestDependencyOrigin::crates_io(&version), + false, + vec![], + false, + ), + )?; + + rustilities::manifest::add_crate_to_dependencies( + roll_runtime_manifest, + &pallet.get_crate_name(), + ManifestDependencyConfig::new( + ManifestDependencyOrigin::workspace(), + false, + vec![], + false, + ), + )?; + + pop_common::manifest::add_pallet_features_to_manifest( + roll_runtime_manifest, + pallet.get_crate_name(), + )?; + rollback.commit()?; if let Some(mut workspace_toml) = pop_common::manifest::find_workspace_toml(&runtime_path) { diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 529c8fe49..9780776e3 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -1,15 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 -use clap::{ - builder::{PossibleValue, TypedValueParser}, - ValueEnum, -}; +use clap::ValueEnum; use proc_macro2::{Literal, TokenStream}; -use semver::Version; -use std::{ffi::OsStr, str::FromStr}; use strum_macros::{EnumIter, EnumMessage}; use syn::{parse_quote, Item}; -#[derive(Debug, Copy, Clone, PartialEq, EnumIter, EnumMessage, ValueEnum)] +#[derive(Debug, Copy, Clone, PartialEq, EnumIter, EnumMessage, ValueEnum, Eq)] pub(crate) enum CommonPallets { /// Add pallet-balances to your runtime. #[strum(message = "balances", detailed_message = "Add pallet-balances to your runtime.")] @@ -106,72 +101,6 @@ impl CommonPallets { } } -impl FromStr for CommonPallets { - type Err = String; - - fn from_str(input: &str) -> Result { - match input.to_lowercase().as_str() { - "balances" => Ok(CommonPallets::Balances), - "contracts" => Ok(CommonPallets::Contracts), - _ => Err(format!("'{}' is not a valid pallet.", input)), - } - } -} - -#[derive(Debug, Clone)] -pub(crate) struct InputPallet { - pub(crate) pallet: CommonPallets, - pub(crate) version: String, -} - -impl FromStr for InputPallet { - type Err = String; - - fn from_str(s: &str) -> Result { - let parts: Vec<&str> = s.split('=').collect(); - if parts.len() != 2 { - return Err(format!("Invalid format: expected =, got '{}'", s)); - } - - let pallet = parts[0] - .parse::() - .map_err(|_| format!("Invalid pallet: '{}'.", parts[0]))?; - - // Not interested in using the Version type at all, just need to know if this &str can be - // parsed as Version - parts[1] - .parse::() - .map_err(|e| format!("Invalid version '{}': {}", parts[1], e))?; - - Ok(InputPallet { pallet, version: parts[1].to_owned() }) - } -} - -#[derive(Clone)] -pub(crate) struct InputPalletParser; - -impl TypedValueParser for InputPalletParser { - type Value = InputPallet; - - fn parse_ref( - &self, - _cmd: &clap::Command, - _arg: Option<&clap::Arg>, - value: &OsStr, - ) -> Result { - let s = value.to_string_lossy(); - s.parse::() - .map_err(|err_msg| clap::Error::raw(clap::error::ErrorKind::InvalidValue, err_msg)) - } - - fn possible_values(&self) -> Option + '_>> { - let iter = CommonPallets::value_variants() - .iter() - .map(|variant| variant.to_possible_value()?); - Some(Box::new(iter)) - } -} - #[cfg(test)] mod tests { use super::*; @@ -287,63 +216,4 @@ mod tests { } ); } - - #[test] - fn common_pallets_from_str_works() { - assert_eq!("balances".parse::().unwrap(), CommonPallets::Balances); - assert_eq!("contracts".parse::().unwrap(), CommonPallets::Contracts); - assert!("invalid".parse::().is_err()); - } - - #[test] - fn input_pallet_from_str_valid_works() { - let input: InputPallet = "balances=1.0.0".parse().unwrap(); - assert_eq!(input.pallet, CommonPallets::Balances); - assert_eq!(input.version, "1.0.0"); - } - - #[test] - fn input_pallet_from_str_invalid_format_fails() { - assert!("balances-1.0.0".parse::().is_err()); - } - - #[test] - fn input_pallet_from_str_invalid_pallet_fails() { - assert!("notapallet=1.0.0".parse::().is_err()); - } - - #[test] - fn input_pallet_from_str_invalid_version_fails() { - assert!("balances=invalid".parse::().is_err()); - } - - #[test] - fn input_pallet_from_str_missing_parts_fails() { - assert!("balances=".parse::().is_err()); - assert!("=1.0.0".parse::().is_err()); - assert!("balances".parse::().is_err()); - assert!("=".parse::().is_err()); - } - - #[test] - fn input_pallet_parser_parse_ref_works() { - let cmd = clap::Command::new("testcmd"); - let arg = clap::Arg::new("pallet"); - let parsed = InputPalletParser - .parse_ref(&cmd, Some(&arg), OsStr::new("contracts=2.0.0")) - .unwrap(); - assert_eq!(parsed.pallet, CommonPallets::Contracts); - assert_eq!(parsed.version, "2.0.0"); - } - - #[test] - fn input_pallet_parser_possible_values_works() { - let possible: Vec = InputPalletParser - .possible_values() - .unwrap() - .map(|pv| pv.get_name().to_owned()) - .collect(); - assert!(possible.contains(&"balances".to_owned())); - assert!(possible.contains(&"contracts".to_owned())); - } } diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index 1419024bd..6d302ab6b 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -8,6 +8,7 @@ use crate::{ use clap::Subcommand; use std::fmt::{Display, Formatter, Result}; +#[cfg(feature = "parachain")] pub(crate) mod add; #[cfg(feature = "parachain")] pub(crate) mod bench; @@ -53,6 +54,7 @@ pub(crate) enum Command { #[clap(alias = "C")] Clean(clean::CleanArgs), /// Add a new feature to your existing polkadot-sdk project + #[cfg(feature = "parachain")] #[clap(name = "add", alias = "a")] Add(add::AddArgs), } @@ -201,6 +203,7 @@ impl Command { }, } }, + #[cfg(feature = "parachain")] Self::Add(args) => match args.command { add::Command::Pallet(cmd) => cmd.execute().await.map(|_| Null), }, @@ -247,6 +250,7 @@ impl Display for Command { Self::Clean(_) => write!(f, "clean"), #[cfg(feature = "parachain")] Self::Bench(args) => write!(f, "bench {}", args.command), + #[cfg(feature = "parachain")] Self::Add(args) => write!(f, "add {:?}", args.command), } } diff --git a/crates/pop-cli/src/common/writer.rs b/crates/pop-cli/src/common/writer.rs index 406567c71..efda32a9c 100644 --- a/crates/pop-cli/src/common/writer.rs +++ b/crates/pop-cli/src/common/writer.rs @@ -28,6 +28,14 @@ pub(crate) enum RuntimeUsedMacro { ConstructRuntime, } +#[derive(Debug, Clone)] +pub(crate) struct PalletConfigRelatedPaths { + pub(crate) runtime_lib_path: PathBuf, + pub(crate) configs_rs_path: PathBuf, + pub(crate) configs_folder_path: PathBuf, + pub(crate) configs_mod_path: PathBuf, +} + // Not more than 256 pallets are included in a runtime pub(crate) type PalletIndex = u8; @@ -110,28 +118,34 @@ pub(crate) fn find_used_runtime_macro(ast: &File) -> anyhow::Result (PathBuf, PathBuf, PathBuf, PathBuf) { +pub(crate) fn compute_pallet_related_paths(runtime_path: &Path) -> PalletConfigRelatedPaths { let runtime_src_path = runtime_path.join("src"); let runtime_lib_path = runtime_src_path.join("lib.rs"); let configs_rs_path = runtime_src_path.join("configs.rs"); let configs_folder_path = runtime_src_path.join("configs"); let configs_mod_path = configs_folder_path.join("mod.rs"); - (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) + PalletConfigRelatedPaths { + runtime_lib_path, + configs_rs_path, + configs_folder_path, + configs_mod_path, + } } // Creates the structure for the path to the file when the new impl block will be added and add it // to an existing rollback. pub(crate) fn create_new_pallet_impl_path_structure<'a>( mut rollback: Rollback<'a>, - runtime_lib_path: &'a Path, - configs_rs_path: &'a Path, - configs_folder_path: &'a Path, - configs_mod_path: &'a Path, + pallet_config_related_paths: &'a PalletConfigRelatedPaths, pallet_config_path: &'a Path, pallet_name: &str, ) -> anyhow::Result> { + let PalletConfigRelatedPaths { + runtime_lib_path, + configs_rs_path, + configs_folder_path, + configs_mod_path, + } = pallet_config_related_paths; let pallet_name_ident = Ident::new(pallet_name, Span::call_site()); let mod_preserver = Preserver::new("mod"); @@ -142,12 +156,12 @@ pub(crate) fn create_new_pallet_impl_path_structure<'a>( match (configs_rs_path.is_file(), configs_mod_path.is_file()) { // The runtime is using a configs module without the mod.rs sintax (true, false) => { - if rollback.get_noted_file(&configs_rs_path).is_none() { - rollback.note_file(&configs_rs_path)?; + if rollback.get_noted_file(configs_rs_path).is_none() { + rollback.note_file(configs_rs_path)?; } let roll_configs_rs_path = rollback - .get_noted_file(&configs_rs_path) + .get_noted_file(configs_rs_path) .expect("This file has been noted above; qed;"); let mut preserved_ast = rust_writer::preserver::preserve_and_parse( roll_configs_rs_path, @@ -162,17 +176,17 @@ pub(crate) fn create_new_pallet_impl_path_structure<'a>( rust_writer::preserver::resolve_preserved(&preserved_ast, roll_configs_rs_path)?; } - rollback.new_file(&pallet_config_path)?; + rollback.new_file(pallet_config_path)?; Ok(rollback) }, // The runtime is using a configs module with the mod.rs syntax (false, true) => { - if rollback.get_noted_file(&configs_mod_path).is_none() { - rollback.note_file(&configs_mod_path)?; + if rollback.get_noted_file(configs_mod_path).is_none() { + rollback.note_file(configs_mod_path)?; } let roll_configs_mod_path = rollback - .get_noted_file(&configs_mod_path) + .get_noted_file(configs_mod_path) .expect("This file has been noted above; qed;"); let mut preserved_ast = rust_writer::preserver::preserve_and_parse( roll_configs_mod_path, @@ -187,7 +201,7 @@ pub(crate) fn create_new_pallet_impl_path_structure<'a>( rust_writer::preserver::resolve_preserved(&preserved_ast, roll_configs_mod_path)?; } - rollback.new_file(&pallet_config_path)?; + rollback.new_file(pallet_config_path)?; Ok(rollback) }, // The runtime isn't using a configs module yet, we opt for the configs.rs @@ -198,12 +212,12 @@ pub(crate) fn create_new_pallet_impl_path_structure<'a>( pub mod configs; ), }; - if rollback.get_noted_file(&runtime_lib_path).is_none() { - rollback.note_file(&runtime_lib_path)?; + if rollback.get_noted_file(runtime_lib_path).is_none() { + rollback.note_file(runtime_lib_path)?; } let roll_runtime_lib_path = rollback - .get_noted_file(&runtime_lib_path) + .get_noted_file(runtime_lib_path) .expect("This file has been noted above; qed;"); let mut preserved_ast = rust_writer::preserver::preserve_and_parse( roll_runtime_lib_path, @@ -218,12 +232,12 @@ pub(crate) fn create_new_pallet_impl_path_structure<'a>( rust_writer::preserver::resolve_preserved(&preserved_ast, roll_runtime_lib_path)?; } - rollback.new_file(&configs_rs_path)?; - rollback.new_dir(&configs_folder_path)?; - rollback.new_file(&pallet_config_path)?; + rollback.new_file(configs_rs_path)?; + rollback.new_dir(configs_folder_path)?; + rollback.new_file(pallet_config_path)?; let roll_configs_rs_path = rollback - .get_new_file(&configs_rs_path) + .get_new_file(configs_rs_path) .expect("The new file has been noted above; qed"); // New file so we can mutate it directly. @@ -364,13 +378,12 @@ mod tests { #[test] fn compute_pallet_related_paths_works() { let original_path = PathBuf::from("test"); - let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = - compute_pallet_related_paths(&original_path); + let paths = compute_pallet_related_paths(&original_path); - assert_eq!(runtime_lib_path, PathBuf::from("test/src/lib.rs")); - assert_eq!(configs_rs_path, PathBuf::from("test/src/configs.rs")); - assert_eq!(configs_folder_path, PathBuf::from("test/src/configs")); - assert_eq!(configs_mod_path, PathBuf::from("test/src/configs/mod.rs")); + assert_eq!(paths.runtime_lib_path, PathBuf::from("test/src/lib.rs")); + assert_eq!(paths.configs_rs_path, PathBuf::from("test/src/configs.rs")); + assert_eq!(paths.configs_folder_path, PathBuf::from("test/src/configs")); + assert_eq!(paths.configs_mod_path, PathBuf::from("test/src/configs/mod.rs")); } #[test] @@ -378,23 +391,19 @@ mod tests { let temp_dir = setup_template_construct_runtime_macro() .expect("Failed to setup template and instantiate"); - let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = - compute_pallet_related_paths(&temp_dir.path().join("runtime")); + let paths = compute_pallet_related_paths(&temp_dir.path().join("runtime")); - let pallet_config_path = configs_folder_path.join("test.rs"); + let pallet_config_path = paths.configs_folder_path.join("test.rs"); let pallet_name = "test"; let mut rollback = Rollback::default(); assert!(!pallet_config_path.exists()); - let configs_mod_before = std::fs::read_to_string(&configs_mod_path).unwrap(); + let configs_mod_before = std::fs::read_to_string(&paths.configs_mod_path).unwrap(); rollback = create_new_pallet_impl_path_structure( rollback, - &runtime_lib_path, - &configs_rs_path, - &configs_folder_path, - &configs_mod_path, + &paths, &pallet_config_path, pallet_name, ) @@ -402,7 +411,7 @@ mod tests { rollback.commit().expect("Failed to commit changes"); - let configs_mod_after = std::fs::read_to_string(&configs_mod_path).unwrap(); + let configs_mod_after = std::fs::read_to_string(&paths.configs_mod_path).unwrap(); let configs_mod_diff = TextDiff::from_lines(&configs_mod_before, &configs_mod_after); @@ -426,28 +435,24 @@ mod tests { let temp_dir = setup_template_construct_runtime_macro() .expect("Failed to setup template and instantiate"); - let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = - compute_pallet_related_paths(&temp_dir.path().join("runtime")); + let paths = compute_pallet_related_paths(&temp_dir.path().join("runtime")); - let pallet_config_path = configs_folder_path.join("test.rs"); + let pallet_config_path = &paths.configs_folder_path.join("test.rs"); let pallet_name = "test"; let mut rollback = Rollback::default(); // Create a configs.rs file at the runtime level and delete the mod.rs file, to get a // template where the file configs.rs exists and then is used as the configs module root - std::fs::remove_file(&configs_mod_path).unwrap(); - std::fs::File::create(&configs_rs_path).unwrap(); + std::fs::remove_file(&paths.configs_mod_path).unwrap(); + std::fs::File::create(&paths.configs_rs_path).unwrap(); assert!(!pallet_config_path.exists()); - let configs_rs_before = std::fs::read_to_string(&configs_rs_path).unwrap(); + let configs_rs_before = std::fs::read_to_string(&paths.configs_rs_path).unwrap(); rollback = create_new_pallet_impl_path_structure( rollback, - &runtime_lib_path, - &configs_rs_path, - &configs_folder_path, - &configs_mod_path, + &paths, &pallet_config_path, pallet_name, ) @@ -455,7 +460,7 @@ mod tests { rollback.commit().expect("Failed to commit changes"); - let configs_rs_after = std::fs::read_to_string(&configs_rs_path).unwrap(); + let configs_rs_after = std::fs::read_to_string(&paths.configs_rs_path).unwrap(); let configs_rs_diff = TextDiff::from_lines(&configs_rs_before, &configs_rs_after); @@ -479,28 +484,24 @@ mod tests { let temp_dir = setup_template_construct_runtime_macro() .expect("Failed to setup template and instantiate"); - let (runtime_lib_path, configs_rs_path, configs_folder_path, configs_mod_path) = - compute_pallet_related_paths(&temp_dir.path().join("runtime")); + let paths = compute_pallet_related_paths(&temp_dir.path().join("runtime")); - let pallet_config_path = configs_folder_path.join("test.rs"); + let pallet_config_path = &paths.configs_folder_path.join("test.rs"); let pallet_name = "test"; let mut rollback = Rollback::default(); // Remove configs from the template and clean the lib path (the only interesting thing here // is that pub mod configs; is added to that file). - std::fs::remove_dir_all(&configs_folder_path).unwrap(); - std::fs::File::create(&runtime_lib_path).unwrap(); + std::fs::remove_dir_all(&paths.configs_folder_path).unwrap(); + std::fs::File::create(&paths.runtime_lib_path).unwrap(); assert!(!pallet_config_path.exists()); - let runtime_lib_before = std::fs::read_to_string(&runtime_lib_path).unwrap(); + let runtime_lib_before = std::fs::read_to_string(&paths.runtime_lib_path).unwrap(); rollback = create_new_pallet_impl_path_structure( rollback, - &runtime_lib_path, - &configs_rs_path, - &configs_folder_path, - &configs_mod_path, + &paths, &pallet_config_path, pallet_name, ) @@ -508,7 +509,7 @@ mod tests { rollback.commit().expect("Failed to commit changes"); - let runtime_lib_after = std::fs::read_to_string(&runtime_lib_path).unwrap(); + let runtime_lib_after = std::fs::read_to_string(&paths.runtime_lib_path).unwrap(); let runtime_lib_diff = TextDiff::from_lines(&runtime_lib_before, &runtime_lib_after); @@ -524,8 +525,8 @@ mod tests { } assert!(pallet_config_path.exists()); - assert!(configs_folder_path.is_dir()); - assert_eq!(std::fs::read_to_string(&configs_rs_path).unwrap(), "mod test;\n"); + assert!(&paths.configs_folder_path.is_dir()); + assert_eq!(std::fs::read_to_string(&paths.configs_rs_path).unwrap(), "mod test;\n"); assert_eq!(expected_inserted_lines, inserted_lines); } } diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index b0ba0f9b1..4e9dced1c 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -25,6 +25,8 @@ async fn parachain_lifecycle() -> Result<()> { "new", "parachain", "test_parachain", + "--release-tag", + "polkadot-stable2412", "--symbol", "POP", "--decimals", @@ -60,7 +62,7 @@ async fn parachain_lifecycle() -> Result<()> { Command::cargo_bin("pop") .unwrap() .current_dir(&test_parachain) - .args(&["add", "pallet", "-p", "contracts=39.0.0"]) + .args(&["add", "pallet", "-p", "contracts", "-v", "39.0.0"]) .assert() .success(); @@ -168,12 +170,11 @@ impl pallet_contracts::Config for Runtime { assert!(temp_dir.join("test_parachain/target").exists()); - let temp_parachain_dir = temp_dir.join("test_parachain"); // pop build spec --output ./target/pop/test-spec.json --id 2222 --type development --relay // paseo-local --protocol-id pop-protocol" --chain local --skip-deterministic-build Command::cargo_bin("pop") .unwrap() - .current_dir(&temp_parachain_dir) + .current_dir(&test_parachain) .args(&[ "build", "spec", @@ -199,13 +200,13 @@ impl pallet_contracts::Config for Runtime { .success(); // Assert build files have been generated - assert!(temp_parachain_dir.join("target").exists()); - assert!(temp_parachain_dir.join("target/pop/test-spec.json").exists()); - assert!(temp_parachain_dir.join("target/pop/test-spec-raw.json").exists()); - assert!(temp_parachain_dir.join("target/pop/para-2222.wasm").exists()); - assert!(temp_parachain_dir.join("target/pop/para-2222-genesis-state").exists()); + assert!(test_parachain.join("target").exists()); + assert!(test_parachain.join("target/pop/test-spec.json").exists()); + assert!(test_parachain.join("target/pop/test-spec-raw.json").exists()); + assert!(test_parachain.join("target/pop/para-2222.wasm").exists()); + assert!(test_parachain.join("target/pop/para-2222-genesis-state").exists()); - let content = fs::read_to_string(temp_parachain_dir.join("target/pop/test-spec-raw.json")) + let content = fs::read_to_string(test_parachain.join("target/pop/test-spec-raw.json")) .expect("Could not read file"); // Assert custom values has been set propertly assert!(content.contains("\"para_id\": 2222")); @@ -216,8 +217,8 @@ impl pallet_contracts::Config for Runtime { assert!(content.contains("\"id\": \"local_testnet\"")); // Overwrite the config file to manually set the port to test pop call parachain. - let network_toml_path = temp_parachain_dir.join("network.toml"); - fs::create_dir_all(&temp_parachain_dir)?; + let network_toml_path = test_parachain.join("network.toml"); + fs::create_dir_all(&test_parachain)?; let random_port = find_free_port(None); let localhost_url = format!("ws://127.0.0.1:{}", random_port); fs::write( @@ -248,7 +249,7 @@ name = "collator-01" // `pop up network -f ./network.toml --skip-confirm` let mut cmd = Cmd::new(cargo_bin("pop")) - .current_dir(&temp_parachain_dir) + .current_dir(&test_parachain) .args(&["up", "network", "-f", "./network.toml", "--skip-confirm"]) .spawn() .unwrap(); diff --git a/crates/pop-common/Cargo.toml b/crates/pop-common/Cargo.toml index b9a82a3cd..d1403bd94 100644 --- a/crates/pop-common/Cargo.toml +++ b/crates/pop-common/Cargo.toml @@ -38,4 +38,4 @@ url.workspace = true [dev-dependencies] mockito.workspace = true tempfile.workspace = true -similar = "2.7.0" +similar.workspace = true diff --git a/crates/pop-common/src/errors.rs b/crates/pop-common/src/errors.rs index ed28883ad..8729f70a3 100644 --- a/crates/pop-common/src/errors.rs +++ b/crates/pop-common/src/errors.rs @@ -45,6 +45,9 @@ pub enum Error { /// An error occurred while executing a test command. #[error("Failed to execute test command: {0}")] TestCommand(String), + /// An error coming from the toml_edit crate + #[error("toml_edit: {0}")] + TomlEdit(#[from] toml_edit::TomlError), /// The command is unsupported. #[error("Unsupported command: {0}")] UnsupportedCommand(String), diff --git a/crates/pop-common/src/manifest.rs b/crates/pop-common/src/manifest.rs index 67428220a..d37647b53 100644 --- a/crates/pop-common/src/manifest.rs +++ b/crates/pop-common/src/manifest.rs @@ -161,17 +161,17 @@ pub fn add_feature(project: &Path, (key, items): (String, Vec)) -> anyho pub fn add_pallet_features_to_manifest>( manifest_path: P, pallet_crate_name: String, -) -> anyhow::Result<()> { +) -> Result<(), Error> { fn do_add_pallet_features_to_manifest( manifest_path: &Path, pallet_crate_name: String, - ) -> anyhow::Result<()> { + ) -> Result<(), Error> { let cargo_toml_content = std::fs::read_to_string(manifest_path)?; let mut doc = cargo_toml_content.parse::()?; if !doc.as_table().contains_key("features") { - return Err(anyhow::anyhow!( - "The runtime manifest does not contain a [features] section" + return Err(Error::Config( + "The runtime manifest does not contain a [features] section".to_owned(), )); } @@ -183,24 +183,28 @@ pub fn add_pallet_features_to_manifest>( let features_table = doc.get_mut("features").and_then(|item| item.as_table_mut()).ok_or_else(|| { - anyhow::anyhow!("The runtime manifest does not contain a valid [features] table") + Error::Config( + "The runtime manifest does not contain a valid [features] table".to_owned(), + ) })?; for (feature, dep_feature) in features_to_add { let feature_item = features_table.get_mut(feature).ok_or_else(|| { - anyhow::anyhow!(format!( + Error::Config(format!( "Feature `{}` does not exist in the runtime manifest", feature )) })?; if feature_item.is_array() { - let array = feature_item.as_array_mut()?; + let array = feature_item + .as_array_mut() + .expect("feature_item is an array, so as_array_mut is always some; qed"); if !array.iter().any(|v| v.as_str() == Some(&dep_feature)) { array.push(dep_feature); } } else { - return Err(anyhow::anyhow!(format!( + return Err(Error::Config(format!( "Feature `{}` is not an array in the runtime manifest", feature ))); From f5ba569006e48d1f53d3e094a5460854bdf01e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= <117524919+tsenovilla@users.noreply.github.com> Date: Tue, 29 Apr 2025 20:29:50 +0200 Subject: [PATCH 29/38] writer compiles only in parachain feature --- crates/pop-cli/src/common/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/pop-cli/src/common/mod.rs b/crates/pop-cli/src/common/mod.rs index 02fce3185..00b5feb7b 100644 --- a/crates/pop-cli/src/common/mod.rs +++ b/crates/pop-cli/src/common/mod.rs @@ -21,6 +21,7 @@ pub mod runtime; pub mod try_runtime; #[cfg(feature = "wallet-integration")] pub mod wallet; +#[cfg(feature = "parachain")] pub(crate) mod writer; use std::fmt::{Display, Formatter, Result}; From 037bb0b80eaf293787f60affcef26030bfaaaa6e Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Wed, 14 May 2025 10:40:03 +0200 Subject: [PATCH 30/38] chore: add assets pallet --- .../src/commands/add/pallet/common_pallets.rs | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 9780776e3..2914578ae 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -6,35 +6,35 @@ use syn::{parse_quote, Item}; #[derive(Debug, Copy, Clone, PartialEq, EnumIter, EnumMessage, ValueEnum, Eq)] pub(crate) enum CommonPallets { - /// Add pallet-balances to your runtime. - #[strum(message = "balances", detailed_message = "Add pallet-balances to your runtime.")] - Balances, - /// Add pallet-contracts to your runtime. - #[strum(message = "contracts", detailed_message = "Add pallet-contracts to your runtime.")] + /// A simple, secure module for dealing with fungible assets. + #[strum(message = "assets", detailed_message = "A simple, secure module for dealing with fungible assets..")] + Assets, + /// The Contracts module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts. + #[strum(message = "contracts", detailed_message = "The Contracts module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts.")] Contracts, } impl CommonPallets { pub(crate) fn get_crate_name(&self) -> String { match self { - CommonPallets::Balances => "pallet-balances".to_owned(), + CommonPallets::Assets => "pallet-assets".to_owned(), CommonPallets::Contracts => "pallet-contracts".to_owned(), } } pub(crate) fn get_pallet_declaration_construct_runtime(&self) -> TokenStream { match self { - CommonPallets::Balances => parse_quote! { Balances: pallet_balances, }, + CommonPallets::Assets => parse_quote! { Assets: pallet_assets, }, CommonPallets::Contracts => parse_quote! { Contracts: pallet_contracts, }, } } pub(crate) fn get_pallet_declaration_runtime_module(&self, highest_index: Literal) -> Item { match self { - CommonPallets::Balances => parse_quote! { + CommonPallets::Assets => parse_quote! { ///TEMP_DOC #[runtime::pallet_index(#highest_index)] - pub type Balances = pallet_balances; + pub type Assets = pallet_assets; }, CommonPallets::Contracts => parse_quote! { ///TEMP_DOC @@ -46,13 +46,16 @@ impl CommonPallets { pub(crate) fn get_impl_needed_use_statements(&self) -> Vec { match self { - CommonPallets::Balances => vec![ + CommonPallets::Assets => vec![ parse_quote!( ///TEMP_DOC - use crate::{System, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + use crate::{AccountId, Balances, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; ), parse_quote!( - use frame_support::{parameter_types, derive_impl}; + use frame_support::{parameter_types, derive_impl, traits::AsEnsureOriginWithArg}; + ), + parse_quote!( + use frame_system::{EnsureRoot, EnsureSigned}; ), ], CommonPallets::Contracts => vec![ @@ -69,7 +72,7 @@ impl CommonPallets { pub(crate) fn get_needed_parameter_types(&self) -> Item { match self { - CommonPallets::Balances => Item::Verbatim(TokenStream::new()), + CommonPallets::Assets => Item::Verbatim(TokenStream::new()), CommonPallets::Contracts => parse_quote! { ///TEMP_DOC parameter_types!{ @@ -81,11 +84,13 @@ impl CommonPallets { pub(crate) fn get_needed_impl_block(&self) -> Item { match self { - CommonPallets::Balances => parse_quote! { + CommonPallets::Assets => parse_quote! { ///TEMP_DOC - #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] - impl pallet_balances::Config for Runtime{ - type AccountStore = System; + #[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)] + impl pallet_assets::Config for Runtime{ + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; } }, CommonPallets::Contracts => parse_quote! { @@ -107,15 +112,15 @@ mod tests { #[test] fn get_crate_name_works() { - assert_eq!(CommonPallets::Balances.get_crate_name(), "pallet-balances"); + assert_eq!(CommonPallets::Assets.get_crate_name(), "pallet-assets"); assert_eq!(CommonPallets::Contracts.get_crate_name(), "pallet-contracts"); } #[test] fn get_pallet_declaration_construct_runtime_works() { assert!(rustilities::parsing::syntactic_token_stream_compare( - CommonPallets::Balances.get_pallet_declaration_construct_runtime(), - parse_quote! { Balances: pallet_balances, } + CommonPallets::Assets.get_pallet_declaration_construct_runtime(), + parse_quote! { Assets: pallet_assets, } )); assert!(rustilities::parsing::syntactic_token_stream_compare( @@ -127,11 +132,11 @@ mod tests { #[test] fn get_pallet_declaration_runtime_module_works() { assert_eq!( - CommonPallets::Balances.get_pallet_declaration_runtime_module(parse_quote!(1)), + CommonPallets::Assets.get_pallet_declaration_runtime_module(parse_quote!(1)), parse_quote! { ///TEMP_DOC #[runtime::pallet_index(1)] - pub type Balances = pallet_balances; + pub type Assets = pallet_assets; } ); assert_eq!( @@ -147,14 +152,17 @@ mod tests { #[test] fn get_impl_needed_use_statements_works() { assert_eq!( - CommonPallets::Balances.get_impl_needed_use_statements(), + CommonPallets::Assets.get_impl_needed_use_statements(), vec![ parse_quote! { ///TEMP_DOC - use crate::{System, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + use crate::{AccountId, Balances, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; }, parse_quote!( - use frame_support::{parameter_types, derive_impl}; + use frame_support::{parameter_types, derive_impl, traits::AsEnsureOriginWithArg}; + ), + parse_quote!( + use frame_system::{EnsureRoot, EnsureSigned}; ) ] ); @@ -175,7 +183,7 @@ mod tests { #[test] fn get_needed_parameter_types_works() { assert_eq!( - CommonPallets::Balances.get_needed_parameter_types(), + CommonPallets::Assets.get_needed_parameter_types(), Item::Verbatim(TokenStream::new()) ); @@ -193,12 +201,14 @@ mod tests { #[test] fn get_needed_impl_block_works() { assert_eq!( - CommonPallets::Balances.get_needed_impl_block(), + CommonPallets::Assets.get_needed_impl_block(), parse_quote! { ///TEMP_DOC - #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] - impl pallet_balances::Config for Runtime { - type AccountStore = System; + #[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)] + impl pallet_assets::Config for Runtime { + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; } } ); From ec5399d19b7bc96bdcfbcd560ccbde648e63d513 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 20 May 2025 14:54:04 +0200 Subject: [PATCH 31/38] feat: add revive config --- .../src/commands/add/pallet/common_pallets.rs | 88 +++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 2914578ae..a0b583b30 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -7,11 +7,25 @@ use syn::{parse_quote, Item}; #[derive(Debug, Copy, Clone, PartialEq, EnumIter, EnumMessage, ValueEnum, Eq)] pub(crate) enum CommonPallets { /// A simple, secure module for dealing with fungible assets. - #[strum(message = "assets", detailed_message = "A simple, secure module for dealing with fungible assets..")] + #[strum( + message = "assets", + detailed_message = "A simple, secure module for dealing with fungible assets.." + )] Assets, - /// The Contracts module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts. - #[strum(message = "contracts", detailed_message = "The Contracts module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts.")] + /// The Contracts module provides functionality for the runtime to deploy and execute + /// WebAssembly smart-contracts. + #[strum( + message = "contracts", + detailed_message = "The Contracts module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts." + )] Contracts, + /// Experimental module that provides functionality for the runtime to deploy and execute + /// PolkaVM smart-contracts. + #[strum( + message = "revive", + detailed_message = "Experimental module that provides functionality for the runtime to deploy and execute PolkaVM smart-contracts." + )] + Revive, } impl CommonPallets { @@ -19,6 +33,7 @@ impl CommonPallets { match self { CommonPallets::Assets => "pallet-assets".to_owned(), CommonPallets::Contracts => "pallet-contracts".to_owned(), + CommonPallets::Revive => "pallet-revive".to_owned(), } } @@ -26,6 +41,7 @@ impl CommonPallets { match self { CommonPallets::Assets => parse_quote! { Assets: pallet_assets, }, CommonPallets::Contracts => parse_quote! { Contracts: pallet_contracts, }, + CommonPallets::Revive => parse_quote! { Revive: pallet_revive, }, } } @@ -41,6 +57,11 @@ impl CommonPallets { #[runtime::pallet_index(#highest_index)] pub type Contracts = pallet_contracts; }, + CommonPallets::Revive => parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(#highest_index)] + pub type Revive = pallet_revive; + }, } } @@ -67,6 +88,15 @@ impl CommonPallets { use frame_support::{parameter_types, derive_impl}; ), ], + CommonPallets::Revive => vec![ + parse_quote!( + ///TEMP_DOC + use crate::{Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + ), + parse_quote!( + use frame_support::{parameter_types, derive_impl}; + ), + ], } } @@ -79,6 +109,7 @@ impl CommonPallets { pub Schedule: pallet_contracts::Schedule = >::default(); } }, + CommonPallets::Revive => Item::Verbatim(TokenStream::new()), } } @@ -102,6 +133,14 @@ impl CommonPallets { type CallStack = [pallet_contracts::Frame; 5]; } }, + CommonPallets::Revive => parse_quote! { + ///TEMP_DOC + #[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] + impl pallet_revive::Config for Runtime{ + type Currency = Balances; + type AddressMapper = pallet_revive::AccountId32Mapper; + } + }, } } } @@ -114,6 +153,7 @@ mod tests { fn get_crate_name_works() { assert_eq!(CommonPallets::Assets.get_crate_name(), "pallet-assets"); assert_eq!(CommonPallets::Contracts.get_crate_name(), "pallet-contracts"); + assert_eq!(CommonPallets::Revive.get_crate_name(), "pallet-revive"); } #[test] @@ -127,6 +167,11 @@ mod tests { CommonPallets::Contracts.get_pallet_declaration_construct_runtime(), parse_quote! { Contracts: pallet_contracts, } )); + + assert!(rustilities::parsing::syntactic_token_stream_compare( + CommonPallets::Revive.get_pallet_declaration_construct_runtime(), + parse_quote! { Revive: pallet_revive, } + )); } #[test] @@ -147,6 +192,14 @@ mod tests { pub type Contracts = pallet_contracts; } ); + assert_eq!( + CommonPallets::Revive.get_pallet_declaration_runtime_module(parse_quote!(1)), + parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(1)] + pub type Revive = pallet_revive; + } + ); } #[test] @@ -178,6 +231,18 @@ mod tests { ) ] ); + assert_eq!( + CommonPallets::Revive.get_impl_needed_use_statements(), + vec![ + parse_quote! { + ///TEMP_DOC + use crate::{Runtime, Balances, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + }, + parse_quote!( + use frame_support::{parameter_types, derive_impl}; + ) + ] + ); } #[test] @@ -186,7 +251,6 @@ mod tests { CommonPallets::Assets.get_needed_parameter_types(), Item::Verbatim(TokenStream::new()) ); - assert_eq!( CommonPallets::Contracts.get_needed_parameter_types(), parse_quote! { @@ -196,6 +260,10 @@ mod tests { } } ); + assert_eq!( + CommonPallets::Revive.get_needed_parameter_types(), + Item::Verbatim(TokenStream::new()) + ); } #[test] @@ -212,7 +280,6 @@ mod tests { } } ); - assert_eq!( CommonPallets::Contracts.get_needed_impl_block(), parse_quote! { @@ -225,5 +292,16 @@ mod tests { } } ); + assert_eq!( + CommonPallets::Revive.get_needed_impl_block(), + parse_quote! { + ///TEMP_DOC + #[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] + impl pallet_revive::Config for Runtime{ + type Currency = Balances; + type AddressMapper = pallet_revive::AccountId32Mapper; + } + } + ); } } From ec1c27e93218d2149954cb25ac5b5b438b52f8d0 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 20 May 2025 15:14:37 +0200 Subject: [PATCH 32/38] feat: add utility pallet --- .../src/commands/add/pallet/common_pallets.rs | 79 ++++++++++++++++++- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index a0b583b30..4d8141ab8 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -26,6 +26,12 @@ pub(crate) enum CommonPallets { detailed_message = "Experimental module that provides functionality for the runtime to deploy and execute PolkaVM smart-contracts." )] Revive, + /// A stateless module with helpers for dispatch management which does no re-authentication. + #[strum( + message = "utility", + detailed_message = "A stateless module with helpers for dispatch management which does no re-authentication." + )] + Utility, } impl CommonPallets { @@ -34,6 +40,7 @@ impl CommonPallets { CommonPallets::Assets => "pallet-assets".to_owned(), CommonPallets::Contracts => "pallet-contracts".to_owned(), CommonPallets::Revive => "pallet-revive".to_owned(), + CommonPallets::Utility => "pallet-utility".to_owned(), } } @@ -42,6 +49,7 @@ impl CommonPallets { CommonPallets::Assets => parse_quote! { Assets: pallet_assets, }, CommonPallets::Contracts => parse_quote! { Contracts: pallet_contracts, }, CommonPallets::Revive => parse_quote! { Revive: pallet_revive, }, + CommonPallets::Utility => parse_quote! { Utility: pallet_utility, }, } } @@ -62,6 +70,11 @@ impl CommonPallets { #[runtime::pallet_index(#highest_index)] pub type Revive = pallet_revive; }, + CommonPallets::Utility => parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(#highest_index)] + pub type Utility = pallet_utility; + }, } } @@ -70,7 +83,9 @@ impl CommonPallets { CommonPallets::Assets => vec![ parse_quote!( ///TEMP_DOC - use crate::{AccountId, Balances, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + use crate::{ + AccountId, Balances, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall, + }; ), parse_quote!( use frame_support::{parameter_types, derive_impl, traits::AsEnsureOriginWithArg}; @@ -97,6 +112,12 @@ impl CommonPallets { use frame_support::{parameter_types, derive_impl}; ), ], + CommonPallets::Utility => vec![ + parse_quote!( + ///TEMP_DOC + use crate::{OriginCaller, RuntimeCall, RuntimeEvent}; + ), + ], } } @@ -110,6 +131,7 @@ impl CommonPallets { } }, CommonPallets::Revive => Item::Verbatim(TokenStream::new()), + CommonPallets::Utility => Item::Verbatim(TokenStream::new()), } } @@ -141,6 +163,15 @@ impl CommonPallets { type AddressMapper = pallet_revive::AccountId32Mapper; } }, + CommonPallets::Utility => parse_quote! { + ///TEMP_DOC + impl pallet_utility::Config for Runtime{ + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_utility::weights::SubstrateWeight; + } + }, } } } @@ -154,6 +185,7 @@ mod tests { assert_eq!(CommonPallets::Assets.get_crate_name(), "pallet-assets"); assert_eq!(CommonPallets::Contracts.get_crate_name(), "pallet-contracts"); assert_eq!(CommonPallets::Revive.get_crate_name(), "pallet-revive"); + assert_eq!(CommonPallets::Utility.get_crate_name(), "pallet-utility"); } #[test] @@ -162,16 +194,18 @@ mod tests { CommonPallets::Assets.get_pallet_declaration_construct_runtime(), parse_quote! { Assets: pallet_assets, } )); - assert!(rustilities::parsing::syntactic_token_stream_compare( CommonPallets::Contracts.get_pallet_declaration_construct_runtime(), parse_quote! { Contracts: pallet_contracts, } )); - assert!(rustilities::parsing::syntactic_token_stream_compare( CommonPallets::Revive.get_pallet_declaration_construct_runtime(), parse_quote! { Revive: pallet_revive, } )); + assert!(rustilities::parsing::syntactic_token_stream_compare( + CommonPallets::Utility.get_pallet_declaration_construct_runtime(), + parse_quote! { Utility: pallet_utility, } + )); } #[test] @@ -200,6 +234,14 @@ mod tests { pub type Revive = pallet_revive; } ); + assert_eq!( + CommonPallets::Utility.get_pallet_declaration_runtime_module(parse_quote!(1)), + parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(1)] + pub type Utility = pallet_utility; + } + ); } #[test] @@ -243,6 +285,21 @@ mod tests { ) ] ); + assert_eq!( + CommonPallets::Utility.get_impl_needed_use_statements(), + vec![ + parse_quote!( + ///TEMP_DOC + use crate::{ + AccountId, Balances, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall, + OriginCaller, + }; + ), + parse_quote!( + use frame_system::{EnsureRoot, EnsureSigned}; + ), + ] + ); } #[test] @@ -264,6 +321,10 @@ mod tests { CommonPallets::Revive.get_needed_parameter_types(), Item::Verbatim(TokenStream::new()) ); + assert_eq!( + CommonPallets::Utility.get_needed_parameter_types(), + Item::Verbatim(TokenStream::new()) + ); } #[test] @@ -303,5 +364,17 @@ mod tests { } } ); + assert_eq!( + CommonPallets::Utility.get_needed_impl_block(), + parse_quote! { + ///TEMP_DOC + impl pallet_utility::Config for Runtime{ + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_utility::weights::SubstrateWeight; + } + } + ); } } From 3a4b95dd8169e42a10364424e12a6bc031845ba1 Mon Sep 17 00:00:00 2001 From: AlexD10S Date: Tue, 20 May 2025 15:28:58 +0200 Subject: [PATCH 33/38] feat: add sudo pallet --- .../src/commands/add/pallet/common_pallets.rs | 77 ++++++++++++++----- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs index 4d8141ab8..2ae29146f 100644 --- a/crates/pop-cli/src/commands/add/pallet/common_pallets.rs +++ b/crates/pop-cli/src/commands/add/pallet/common_pallets.rs @@ -26,6 +26,13 @@ pub(crate) enum CommonPallets { detailed_message = "Experimental module that provides functionality for the runtime to deploy and execute PolkaVM smart-contracts." )] Revive, + /// allows for a single account (called the "sudo key") to execute dispatchable functions that + /// require a Root call or designate a new account to replace them as the sudo key. + #[strum( + message = "sudo", + detailed_message = " allows for a single account (called the \"sudo key\") to execute dispatchable functions that require a Root call or designate a new account to replace them as the sudo key." + )] + Sudo, /// A stateless module with helpers for dispatch management which does no re-authentication. #[strum( message = "utility", @@ -40,6 +47,7 @@ impl CommonPallets { CommonPallets::Assets => "pallet-assets".to_owned(), CommonPallets::Contracts => "pallet-contracts".to_owned(), CommonPallets::Revive => "pallet-revive".to_owned(), + CommonPallets::Sudo => "pallet-sudo".to_owned(), CommonPallets::Utility => "pallet-utility".to_owned(), } } @@ -49,6 +57,7 @@ impl CommonPallets { CommonPallets::Assets => parse_quote! { Assets: pallet_assets, }, CommonPallets::Contracts => parse_quote! { Contracts: pallet_contracts, }, CommonPallets::Revive => parse_quote! { Revive: pallet_revive, }, + CommonPallets::Sudo => parse_quote! { Sudo: pallet_sudo, }, CommonPallets::Utility => parse_quote! { Utility: pallet_utility, }, } } @@ -70,6 +79,11 @@ impl CommonPallets { #[runtime::pallet_index(#highest_index)] pub type Revive = pallet_revive; }, + CommonPallets::Sudo => parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(#highest_index)] + pub type Sudo = pallet_sudo; + }, CommonPallets::Utility => parse_quote! { ///TEMP_DOC #[runtime::pallet_index(#highest_index)] @@ -112,12 +126,11 @@ impl CommonPallets { use frame_support::{parameter_types, derive_impl}; ), ], - CommonPallets::Utility => vec![ - parse_quote!( - ///TEMP_DOC - use crate::{OriginCaller, RuntimeCall, RuntimeEvent}; - ), - ], + CommonPallets::Utility => vec![parse_quote!( + ///TEMP_DOC + use crate::{OriginCaller, RuntimeCall, RuntimeEvent}; + )], + CommonPallets::Sudo => vec![], } } @@ -131,6 +144,7 @@ impl CommonPallets { } }, CommonPallets::Revive => Item::Verbatim(TokenStream::new()), + CommonPallets::Sudo => Item::Verbatim(TokenStream::new()), CommonPallets::Utility => Item::Verbatim(TokenStream::new()), } } @@ -163,6 +177,11 @@ impl CommonPallets { type AddressMapper = pallet_revive::AccountId32Mapper; } }, + CommonPallets::Sudo => parse_quote! { + ///TEMP_DOC + #[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig)] + impl pallet::Config for Runtime{} + }, CommonPallets::Utility => parse_quote! { ///TEMP_DOC impl pallet_utility::Config for Runtime{ @@ -185,6 +204,7 @@ mod tests { assert_eq!(CommonPallets::Assets.get_crate_name(), "pallet-assets"); assert_eq!(CommonPallets::Contracts.get_crate_name(), "pallet-contracts"); assert_eq!(CommonPallets::Revive.get_crate_name(), "pallet-revive"); + assert_eq!(CommonPallets::Sudo.get_crate_name(), "pallet-sudo"); assert_eq!(CommonPallets::Utility.get_crate_name(), "pallet-utility"); } @@ -202,6 +222,10 @@ mod tests { CommonPallets::Revive.get_pallet_declaration_construct_runtime(), parse_quote! { Revive: pallet_revive, } )); + assert!(rustilities::parsing::syntactic_token_stream_compare( + CommonPallets::Sudo.get_pallet_declaration_construct_runtime(), + parse_quote! { Sudo: pallet_sudo, } + )); assert!(rustilities::parsing::syntactic_token_stream_compare( CommonPallets::Utility.get_pallet_declaration_construct_runtime(), parse_quote! { Utility: pallet_utility, } @@ -234,6 +258,14 @@ mod tests { pub type Revive = pallet_revive; } ); + assert_eq!( + CommonPallets::Sudo.get_pallet_declaration_runtime_module(parse_quote!(1)), + parse_quote! { + ///TEMP_DOC + #[runtime::pallet_index(1)] + pub type Sudo = pallet_sudo; + } + ); assert_eq!( CommonPallets::Utility.get_pallet_declaration_runtime_module(parse_quote!(1)), parse_quote! { @@ -251,7 +283,9 @@ mod tests { vec![ parse_quote! { ///TEMP_DOC - use crate::{AccountId, Balances, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall}; + use crate::{ + AccountId, Balances, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall, + }; }, parse_quote!( use frame_support::{parameter_types, derive_impl, traits::AsEnsureOriginWithArg}; @@ -285,20 +319,13 @@ mod tests { ) ] ); + assert_eq!(CommonPallets::Sudo.get_impl_needed_use_statements(), vec![]); assert_eq!( CommonPallets::Utility.get_impl_needed_use_statements(), - vec![ - parse_quote!( - ///TEMP_DOC - use crate::{ - AccountId, Balances, Runtime, RuntimeEvent, RuntimeHoldReason, RuntimeCall, - OriginCaller, - }; - ), - parse_quote!( - use frame_system::{EnsureRoot, EnsureSigned}; - ), - ] + vec![parse_quote!( + ///TEMP_DOC + use crate::{OriginCaller, RuntimeCall, RuntimeEvent}; + ),] ); } @@ -321,6 +348,10 @@ mod tests { CommonPallets::Revive.get_needed_parameter_types(), Item::Verbatim(TokenStream::new()) ); + assert_eq!( + CommonPallets::Sudo.get_needed_parameter_types(), + Item::Verbatim(TokenStream::new()) + ); assert_eq!( CommonPallets::Utility.get_needed_parameter_types(), Item::Verbatim(TokenStream::new()) @@ -364,6 +395,14 @@ mod tests { } } ); + assert_eq!( + CommonPallets::Sudo.get_needed_impl_block(), + parse_quote! { + ///TEMP_DOC + #[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig)] + impl pallet::Config for Runtime{} + } + ); assert_eq!( CommonPallets::Utility.get_needed_impl_block(), parse_quote! { From 97c41c48d0427e037c1ddec91ce815a17dbee594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Wed, 4 Jun 2025 11:48:30 +0200 Subject: [PATCH 34/38] Remove pending HEAD line --- crates/pop-cli/tests/parachain.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index bf136bd27..12efe19af 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -38,7 +38,6 @@ fn generate_all_the_templates() -> Result<()> { Ok(()) } -<<<<<<< HEAD /// Test the parachain lifecycle: new, add pallet ,build, up, call. #[tokio::test] async fn parachain_lifecycle() -> Result<()> { From 2700680a9b14803cdc7e44e5a17d01fd3d6417de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Wed, 4 Jun 2025 11:49:02 +0200 Subject: [PATCH 35/38] fmt --- crates/pop-cli/tests/parachain.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 12efe19af..165dab1de 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -7,7 +7,7 @@ use assert_cmd::cargo::cargo_bin; use pop_common::{find_free_port, templates::Template}; use pop_parachains::Parachain; use similar::{ChangeTag, TextDiff}; -use std::{fs, path::Path, process::Command, ffi::OsStr, thread::sleep, time::Duration}; +use std::{ffi::OsStr, fs, path::Path, process::Command, thread::sleep, time::Duration}; use strum::VariantArray; // Test that all templates are generated correctly @@ -56,18 +56,18 @@ async fn parachain_lifecycle() -> Result<()> { let mut command = pop( &temp_dir, &[ - "new", - "parachain", - "test_parachain", - "--release-tag", - "polkadot-stable2412", - "--symbol", - "POP", - "--decimals", - "6", - "--endowment", - "1u64 << 60", - "--verify", + "new", + "parachain", + "test_parachain", + "--release-tag", + "polkadot-stable2412", + "--symbol", + "POP", + "--decimals", + "6", + "--endowment", + "1u64 << 60", + "--verify", ], ); assert!(command.spawn()?.wait()?.success()); From 7fc818dd7567a2ad39635ea2f8695b890f5e3897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Wed, 4 Jun 2025 13:15:29 +0200 Subject: [PATCH 36/38] Using directly release tags from the polkadot sdk rather than pallet versions --- crates/pop-cli/src/commands/add/pallet.rs | 21 +++++++++++---------- crates/pop-cli/tests/parachain.rs | 11 ++++------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/crates/pop-cli/src/commands/add/pallet.rs b/crates/pop-cli/src/commands/add/pallet.rs index 9b7e0ecad..5f3bb531d 100644 --- a/crates/pop-cli/src/commands/add/pallet.rs +++ b/crates/pop-cli/src/commands/add/pallet.rs @@ -44,10 +44,12 @@ pub struct AddPalletCommand { help = "pop add pallet will place the impl blocks for your pallets' Config traits inside a dedicated file under the configs directory. Use this argument to place them somewhere else." )] pub(crate) pallet_impl_path: Option, - #[arg(long, short, help = "The pallet version.")] - pub(crate) version: Option, + #[arg(long, help = "The release of the polkadot-sdk used by your project.")] + pub(crate) release_tag: Option, } +const POLKADOT_SDK_URL: &str = "https://github.com/paritytech/polkadot-sdk"; + const INVALID_DIR_MSG: &str = "Make sure to run this command either in a runtime crate contained in a workspace, in the workspace itself or to specify the path to the runtime crate using -r."; impl AddPalletCommand { @@ -55,8 +57,8 @@ impl AddPalletCommand { Cli.intro("Add a new pallet to your runtime")?; let mut cmd = Command::new(""); - let (pallet, version) = match (self.pallet, self.version) { - (Some(pallet), Some(version)) => (pallet, version), + let (pallet, release_tag) = match (self.pallet, self.release_tag) { + (Some(pallet), Some(release_tag)) => (pallet, release_tag), (None, None) => { let mut pallet_prompt = cliclack::select("Select a pallet to add to your runtime: ".to_owned()); @@ -70,15 +72,14 @@ impl AddPalletCommand { pallet.get_detailed_message().expect("all variants of CommonPallets have detailed_message; qed;"), ); } - let mut version_prompt = cliclack::input("Which version should use your pallet?") - .placeholder("1.0.0") - .default_input("1.0.0"); - (pallet_prompt.interact()?, version_prompt.interact()?) + let mut release_tag_prompt = cliclack::input("Which release_tag should use your pallet?") + .placeholder("stable2412"); + (pallet_prompt.interact()?, release_tag_prompt.interact()?) }, _ => cmd .error( ErrorKind::Io, - "If you specify pallet/version via the command line, both fields must be specified" + "If you specify pallet/release_tag via the command line, both fields must be specified" ) .exit(), }; @@ -268,7 +269,7 @@ impl AddPalletCommand { roll_workspace_manifest, &pallet.get_crate_name(), ManifestDependencyConfig::new( - ManifestDependencyOrigin::crates_io(&version), + ManifestDependencyOrigin::git(POLKADOT_SDK_URL, &release_tag), false, vec![], false, diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index 165dab1de..dd84cb7f8 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -93,12 +93,9 @@ async fn parachain_lifecycle() -> Result<()> { std::fs::read_to_string(&workspace_manifest_path).unwrap(); let runtime_manifest_content_before = std::fs::read_to_string(&runtime_manifest_path).unwrap(); - Command::cargo_bin("pop") - .unwrap() - .current_dir(&test_parachain) - .args(&["add", "pallet", "-p", "contracts", "-v", "39.0.0"]) - .assert() - .success(); + let mut command = + pop(&temp_dir, &["add", "pallet", "-p", "contracts", "--release-tag", "stable2412"]); + assert!(command.spawn()?.wait()?.success()); let runtime_lib_content_after = std::fs::read_to_string(&runtime_lib_path).unwrap(); let pallet_configs_mod_content_after = @@ -125,7 +122,7 @@ async fn parachain_lifecycle() -> Result<()> { ]; let expected_inserted_lines_configs_mod = vec!["mod contracts;\n"]; let expected_inserted_lines_workspace_manifest = - vec!["pallet-contracts = { version = \"39.0.0\", default-features = false }\n"]; + vec!["pallet-contracts = { git = \"https://github.com/paritytech/polkadot-sdk\", branch = \"stable2412\", default-features = false }\n"]; let expected_inserted_lines_runtime_manifest = vec![ "pallet-contracts = { workspace = true, default-features = false }\n", From 0cbf56f2452bb7854dbbbedf08b9320101c9e3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Wed, 4 Jun 2025 13:37:55 +0200 Subject: [PATCH 37/38] Adding experimental flag --- .github/workflows/ci.yml | 2 +- crates/pop-cli/Cargo.toml | 12 ++++++------ crates/pop-cli/src/commands/mod.rs | 8 ++++---- crates/pop-cli/src/common/mod.rs | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a02186874..e3291e673 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -263,4 +263,4 @@ jobs: - name: Run integration tests env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: cargo test --no-default-features --features parachain --test parachain + run: cargo test --no-default-features --features parachain,experimental --test parachain diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index d87d0923c..892151b07 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -20,15 +20,15 @@ console.workspace = true dirs.workspace = true duct.workspace = true env_logger.workspace = true -fs_rollback.workspace = true +fs_rollback = { workspace = true, optional = true } os_info.workspace = true -proc-macro2.workspace = true +proc-macro2 = { workspace = true, optional = true} reqwest.workspace = true -rustilities = { workspace = true, features = ["fmt", "manifest"]} -rust_writer.workspace = true +rustilities = { workspace = true, features = ["fmt", "manifest"], optional = true } +rust_writer = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true -syn = { workspace = true, features = ["parsing"]} +syn = { workspace = true, features = ["parsing"], optional = true } strum.workspace = true strum_macros.workspace = true tempfile.workspace = true @@ -71,7 +71,7 @@ similar.workspace = true default = ["parachain", "telemetry", "wasm-contracts"] contract = ["wasm-contracts"] contracts = ["polkavm-contracts"] -experimental = ["hashing"] +experimental = ["hashing", "syn", "rust_writer", "rustilities", "proc-macro2", "fs_rollback"] hashing = ["dep:sp-core"] parachain = ["dep:pop-parachains", "dep:git2", "dep:regex", "dep:sp-core", "dep:tracing-subscriber", "wallet-integration"] v6 = [] diff --git a/crates/pop-cli/src/commands/mod.rs b/crates/pop-cli/src/commands/mod.rs index 880f2481e..1d8d8da59 100644 --- a/crates/pop-cli/src/commands/mod.rs +++ b/crates/pop-cli/src/commands/mod.rs @@ -10,7 +10,7 @@ use std::fmt::{Display, Formatter, Result}; #[cfg(feature = "parachain")] use {crate::common::Project::Network, up::network::Relay::*}; -#[cfg(feature = "parachain")] +#[cfg(all(feature = "parachain", feature = "experimental"))] pub(crate) mod add; #[cfg(feature = "parachain")] pub(crate) mod bench; @@ -62,7 +62,7 @@ pub(crate) enum Command { #[clap(alias = "C")] Clean(clean::CleanArgs), /// Add a new feature to your existing polkadot-sdk project - #[cfg(feature = "parachain")] + #[cfg(all(feature = "parachain", feature = "experimental"))] #[clap(name = "add", alias = "a")] Add(add::AddArgs), } @@ -248,7 +248,7 @@ impl Command { }, } }, - #[cfg(feature = "parachain")] + #[cfg(all(feature = "parachain", feature = "experimental"))] Self::Add(args) => match args.command { add::Command::Pallet(cmd) => cmd.execute().await.map(|_| Null), }, @@ -318,7 +318,7 @@ impl Display for Command { Self::Clean(_) => write!(f, "clean"), #[cfg(feature = "parachain")] Self::Bench(args) => write!(f, "bench {}", args.command), - #[cfg(feature = "parachain")] + #[cfg(all(feature = "parachain", feature = "experimental"))] Self::Add(args) => write!(f, "add {:?}", args.command), #[cfg(feature = "hashing")] Command::Hash(args) => write!(f, "hash {}", args.command), diff --git a/crates/pop-cli/src/common/mod.rs b/crates/pop-cli/src/common/mod.rs index 78b8855ca..ee90452b9 100644 --- a/crates/pop-cli/src/common/mod.rs +++ b/crates/pop-cli/src/common/mod.rs @@ -21,7 +21,7 @@ pub mod runtime; pub mod try_runtime; #[cfg(feature = "wallet-integration")] pub mod wallet; -#[cfg(feature = "parachain")] +#[cfg(all(feature = "parachain", feature = "experimental"))] pub(crate) mod writer; use std::fmt::{Display, Formatter, Result}; From d0b0620dc4be30c9b329e5164a1f079c6332cc2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senovilla=20Polo?= Date: Wed, 4 Jun 2025 13:44:54 +0200 Subject: [PATCH 38/38] Address test bug --- crates/pop-cli/tests/parachain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/tests/parachain.rs b/crates/pop-cli/tests/parachain.rs index dd84cb7f8..1717ba5fa 100644 --- a/crates/pop-cli/tests/parachain.rs +++ b/crates/pop-cli/tests/parachain.rs @@ -94,7 +94,7 @@ async fn parachain_lifecycle() -> Result<()> { let runtime_manifest_content_before = std::fs::read_to_string(&runtime_manifest_path).unwrap(); let mut command = - pop(&temp_dir, &["add", "pallet", "-p", "contracts", "--release-tag", "stable2412"]); + pop(&working_dir, &["add", "pallet", "-p", "contracts", "--release-tag", "stable2412"]); assert!(command.spawn()?.wait()?.success()); let runtime_lib_content_after = std::fs::read_to_string(&runtime_lib_path).unwrap();