diff --git a/Cargo.lock b/Cargo.lock index 0dfa2c5..4022bf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6058,6 +6058,7 @@ dependencies = [ "polkadot-runtime-common", "runtime-common", "scale-info", + "serde_json", "smallvec", "sp-api", "sp-block-builder", @@ -10645,7 +10646,6 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "pallet-treasury", "parachains-common", "parity-scale-codec", "polkadot-primitives", @@ -10653,6 +10653,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-std", + "staging-xcm", ] [[package]] @@ -14405,6 +14406,7 @@ dependencies = [ "polkadot-runtime-common", "runtime-common", "scale-info", + "serde_json", "smallvec", "sp-api", "sp-block-builder", diff --git a/README.md b/README.md index 8c5ced5..c73f5dc 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,28 @@ In case the script fails to fetch the relay chain runtimes they can also be buil ./zombienet.sh build ``` +### Notes regarding presets + +Presets are predefined configurations that simplify initializing a chain’s genesis state. They +establish key parameters like balances, collator sets, council members, and the sudo (root) account. +By using presets, you can quickly spin up a network for testing or specialized use without manually +defining every genesis detail. + +#### dev + +- Minimal, local-friendly preset with default collators (e.g., “Alith” and “Balthasar”). +- Distributes tokens across several accounts for testing. +- Sets a small council and a single sudo key for convenient local control. + +#### muse/mythos + +- Uses two specific collators. +- Assigns different account balances for a more structured distribution. +- No council members; includes one sudo key for administration. + +These presets are especially useful for quickly preparing Zombienet-based test setups or any local +development scenario without manually customizing every detail. + ### Notes regarding Polkadot.js and Ethereum accounts On Polkadot.js, when bootstraping the local network with zombienet, the normal accounts are not derived from a seed. diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index d4ffbd5..253ebc0 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -25,8 +25,7 @@ sp-runtime = { workspace = true, default-features = false } sp-core = { workspace = true, default-features = false } sp-std = { workspace = true, default-features = false } pallet-balances = { workspace = true, default-features = false } -pallet-treasury = { workspace = true, default-features = false } - +xcm = { workspace = true, default-features = false } parachains-common = { workspace = true, default-features = false } polkadot-primitives = { workspace = true, default-features = false } @@ -45,6 +44,7 @@ std = [ "sp-std/std", "account/std", "pallet-balances/std", + "xcm/std", ] runtime-benchmarks = [ diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 30d4ca6..8d66f4f 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -2,7 +2,7 @@ use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_core::U256; +use sp_core::{Pair, Public, U256}; use sp_runtime::{ traits::{IdentifyAccount, Verify}, @@ -17,6 +17,8 @@ use frame_support::traits::Incrementable; //https://github.com/paritytech/cumulus/tree/master/parachains/common pub use parachains_common::{AuraId, Balance, Block, BlockNumber, Hash}; +extern crate alloc; + pub type Signature = EthereumSignature; /// Use AccountId20 for Ethereum address @@ -78,3 +80,30 @@ impl From for IncrementableU256 { IncrementableU256(U256::from(value)) } } + +/// The default XCM version to set in genesis config. +pub const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; + +/// Helper function to generate a crypto pair from seed. +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&alloc::format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed. +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Generate collator keys from seed. +/// +/// This function's return type must always match the session keys of the chain in tuple format. +pub fn get_collator_keys_from_seed(seed: &str) -> AuraId { + get_from_seed::(seed) +} diff --git a/runtime/mainnet/Cargo.toml b/runtime/mainnet/Cargo.toml index c2613e5..60615ea 100644 --- a/runtime/mainnet/Cargo.toml +++ b/runtime/mainnet/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] substrate-wasm-builder = { workspace = true, optional = true } [dependencies] +serde_json = { workspace = true } hex-literal = { workspace = true } log = { workspace = true, default-features = false } parity-scale-codec = { workspace = true, default-features = false, features = [ @@ -102,6 +103,7 @@ polkadot-primitives = { workspace = true, default-features = false } [features] default = ["std"] std = [ + "serde_json/std", "parity-scale-codec/std", "log/std", "scale-info/std", diff --git a/runtime/mainnet/src/lib.rs b/runtime/mainnet/src/lib.rs index d6a65c9..3b99847 100644 --- a/runtime/mainnet/src/lib.rs +++ b/runtime/mainnet/src/lib.rs @@ -1144,6 +1144,161 @@ mod benches { ); } +pub mod genesis_config_presets { + use super::*; + use frame_support::build_struct_json_patch; + use hex_literal::hex; + use runtime_common::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; + use serde_json::{to_string, Value}; + use sp_core::{crypto::UncheckedInto, ecdsa}; + use sp_genesis_builder::{PresetId, DEV_RUNTIME_PRESET}; + use sp_runtime::Percent; + + pub const MYTHOS_RUNTIME_PRESET: &str = "mythos"; + pub const PARA_ID: u32 = 3369; + + fn create_preset( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec<(AccountId, Balance)>, + council: Vec, + root_key: AccountId, + id: ParaId, + ) -> Value { + build_struct_json_patch!(RuntimeGenesisConfig { + balances: BalancesConfig { balances: endowed_accounts }, + parachain_info: ParachainInfoConfig { parachain_id: id }, + collator_staking: CollatorStakingConfig { + invulnerables: invulnerables + .iter() + .cloned() + .map(|(acc, _)| acc) + .collect::>(), + min_candidacy_bond: 50 * MYTH, + min_stake: 10 * MYTH, + desired_candidates: 6, + collator_reward_percentage: Percent::from_parts(10), + extra_reward: 0, + }, + council: CouncilConfig { members: council }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc, // account id + acc, // validator id + SessionKeys { aura }, // session keys + ) + }) + .collect::>(), + }, + sudo: SudoConfig { key: Some(root_key) }, + polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, + }) + } + + pub fn get_builtin_preset(id: &PresetId) -> Option> { + let preset = match id.as_ref() { + DEV_RUNTIME_PRESET => { + let balance_per_account = (1_000_000_000 * MYTH).saturating_div(6); + create_preset( + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed("Bob"), + ), + ], + vec![ + ( + AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), + balance_per_account, + ), // Alith + ( + AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")), + balance_per_account, + ), // Baltathar + ( + AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")), + balance_per_account, + ), // Charleth + ( + AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")), + balance_per_account, + ), // Dorothy + ( + AccountId::from(hex!("Ff64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB")), + balance_per_account, + ), // Ethan + ( + AccountId::from(hex!("C0F0f4ab324C46e55D02D0033343B4Be8A55532d")), + balance_per_account, + ), // Faith + ], + vec![ + AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")), // Baltathar + AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")), // Charleth + AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")), // Dorothy + ], + AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), + PARA_ID.into(), + ) + }, + MYTHOS_RUNTIME_PRESET => create_preset( + vec![ + ( + hex!("65c39EB8DDC9EA6F2135A28Ea670E97bc3CCc012").into(), + hex!("d609c361de761b4bf8ba1ae4f8e436e74e1324b0a9eac08b34e31413bbd3f27f") + .unchecked_into(), + ), + ( + hex!("B9717024eB621a7AE331F92C3dC63a0aB60031c5").into(), + hex!("8abe92437bf6690bc8f75cea612a5898cd2823c23681b346f776337660316979") + .unchecked_into(), + ), + ( + hex!("F4d1C38f3Be73d7cD2123968141Aec3AbB393153").into(), + hex!("86360126eb30d60c9232206ba78a9fafb2322958bb3a021fa88ba09dfc753802") + .unchecked_into(), + ), + ( + hex!("E4f607AB7fA6b5Fd4f8127E051f151DaBb7279c6").into(), + hex!("b0909f6832d2f5120b874b3e1cbe1b72fb5ccdbc268ba79bebdd8e71ab41e334") + .unchecked_into(), + ), + ], + vec![ + ( + AccountId::from(hex!("742c722892976C23A3919ADC7A4B562169B91E41")), + 1_000 * MYTH, + ), + ( + AccountId::from(hex!("f476dA221b07135b106d923b8884b76b09982B4F")), + 150_000_000 * MYTH, + ), + ], + vec![], + AccountId::from(hex!("742c722892976C23A3919ADC7A4B562169B91E41")), + PARA_ID.into(), + ), + _ => return None, + }; + + Some( + to_string(&preset) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) + } + + pub fn preset_names() -> Vec { + vec![PresetId::from(DEV_RUNTIME_PRESET), PresetId::from(MYTHOS_RUNTIME_PRESET)] + } +} + impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { @@ -1172,11 +1327,11 @@ impl_runtime_apis! { impl sp_genesis_builder::GenesisBuilder for Runtime { fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, self::genesis_config_presets::get_builtin_preset) } fn preset_names() -> Vec { - vec![] + crate::genesis_config_presets::preset_names() } fn build_state(config: Vec) -> sp_genesis_builder::Result { diff --git a/runtime/testnet/Cargo.toml b/runtime/testnet/Cargo.toml index fcbf6d3..32a98dc 100644 --- a/runtime/testnet/Cargo.toml +++ b/runtime/testnet/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] substrate-wasm-builder = { workspace = true, optional = true } [dependencies] +serde_json = { workspace = true } hex-literal = { workspace = true } log = { workspace = true, default-features = false } parity-scale-codec = { workspace = true, default-features = false, features = ["derive"]} @@ -98,6 +99,7 @@ polkadot-primitives = { workspace = true, default-features = false } [features] default = ["std"] std = [ + "serde_json/std", "parity-scale-codec/std", "log/std", "scale-info/std", diff --git a/runtime/testnet/src/lib.rs b/runtime/testnet/src/lib.rs index b12a4d1..3e2c323 100644 --- a/runtime/testnet/src/lib.rs +++ b/runtime/testnet/src/lib.rs @@ -1161,6 +1161,153 @@ mod benches { ); } +pub mod genesis_config_presets { + use super::*; + use frame_support::build_struct_json_patch; + use hex_literal::hex; + use runtime_common::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; + use serde_json::{to_string, Value}; + use sp_core::{crypto::UncheckedInto, ecdsa}; + use sp_genesis_builder::{PresetId, DEV_RUNTIME_PRESET}; + use sp_runtime::Percent; + + pub const MUSE_RUNTIME_PRESET: &str = "muse"; + pub const PARA_ID: u32 = 3369; + + fn create_preset( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec<(AccountId, Balance)>, + council: Vec, + root_key: AccountId, + id: ParaId, + ) -> Value { + build_struct_json_patch!(RuntimeGenesisConfig { + balances: BalancesConfig { balances: endowed_accounts }, + parachain_info: ParachainInfoConfig { parachain_id: id }, + collator_staking: CollatorStakingConfig { + invulnerables: invulnerables + .iter() + .cloned() + .map(|(acc, _)| acc) + .collect::>(), + min_candidacy_bond: 50 * MUSE, + min_stake: 10 * MUSE, + desired_candidates: 6, + collator_reward_percentage: Percent::from_parts(10), + extra_reward: 0, + }, + council: CouncilConfig { members: council }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc, // account id + acc, // validator id + SessionKeys { aura }, // session keys + ) + }) + .collect::>(), + }, + sudo: SudoConfig { key: Some(root_key) }, + polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, + }) + } + + pub fn get_builtin_preset(id: &PresetId) -> Option> { + let preset = match id.as_ref() { + DEV_RUNTIME_PRESET => { + let balance_per_account = (1_000_000_000 * MUSE).saturating_div(6); + create_preset( + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed("Bob"), + ), + ], + vec![ + ( + AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), + balance_per_account, + ), // Alith + ( + AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")), + balance_per_account, + ), // Baltathar + ( + AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")), + balance_per_account, + ), // Charleth + ( + AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")), + balance_per_account, + ), // Dorothy + ( + AccountId::from(hex!("Ff64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB")), + balance_per_account, + ), // Ethan + ( + AccountId::from(hex!("C0F0f4ab324C46e55D02D0033343B4Be8A55532d")), + balance_per_account, + ), // Faith + ], + vec![ + AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")), // Baltathar + AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")), // Charleth + AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")), // Dorothy + ], + AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), + PARA_ID.into(), + ) + }, + MUSE_RUNTIME_PRESET => create_preset( + vec![ + ( + hex!("e6b4f55209a70384db3d147c06b99e32feb03d6fe191ff62b9dd23d5dd9ac64a") + .into(), + hex!("e6b4f55209a70384db3d147c06b99e32feb03d6fe191ff62b9dd23d5dd9ac64a") + .unchecked_into(), + ), + ( + hex!("e07113e692708775d0cc39e00fe7f2974bff4e20a6fd127f0810c01142547723") + .into(), + hex!("e07113e692708775d0cc39e00fe7f2974bff4e20a6fd127f0810c01142547723") + .unchecked_into(), + ), + ], + vec![ + ( + AccountId::from(hex!("16A5094837B65f1177824F0D36002f33d9A2Df7d")), + 150_000_000 * MUSE, + ), + ( + AccountId::from(hex!("8CC95e7DFa96A86D728D2E6EB86400DEfBB56c90")), + 1_000 * MUSE, + ), + ], + vec![], + AccountId::from(hex!("8CC95e7DFa96A86D728D2E6EB86400DEfBB56c90")), + PARA_ID.into(), + ), + _ => return None, + }; + + Some( + to_string(&preset) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) + } + + pub fn preset_names() -> Vec { + vec![PresetId::from(DEV_RUNTIME_PRESET), PresetId::from(MUSE_RUNTIME_PRESET)] + } +} + impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { @@ -1189,11 +1336,11 @@ impl_runtime_apis! { impl sp_genesis_builder::GenesisBuilder for Runtime { fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, self::genesis_config_presets::get_builtin_preset) } fn preset_names() -> Vec { - vec![] + crate::genesis_config_presets::preset_names() } fn build_state(config: Vec) -> sp_genesis_builder::Result {