Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

134 changes: 2 additions & 132 deletions templates/parachain/node/src/chain_spec.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,11 @@
use cumulus_primitives_core::ParaId;
use parachain_template_runtime as runtime;
use runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT};
use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup};
use sc_service::ChainType;
use serde::{Deserialize, Serialize};
use sp_core::{sr25519, Pair, Public};
use sp_runtime::traits::{IdentifyAccount, Verify};

/// Specialized `ChainSpec` for the normal parachain runtime.
pub type ChainSpec = sc_service::GenericChainSpec<Extensions>;

/// The default XCM version to set in genesis config.
const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION;

/// Helper function to generate a crypto pair from seed
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
.expect("static values are valid; qed")
.public()
}

/// The extensions for the [`ChainSpec`].
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)]
pub struct Extensions {
Expand All @@ -38,30 +24,6 @@ impl Extensions {
}
}

type AccountPublic = <Signature as Verify>::Signer;

/// 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::<AuraId>(seed)
}

/// Helper function to generate an account ID from seed
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
where
AccountPublic: From<<TPublic::Pair as Pair>::Public>,
{
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}

/// Generate the session keys from individual elements.
///
/// The input must be a tuple of individual keys (a single arg for now since we have just one key).
pub fn template_session_keys(keys: AuraId) -> runtime::SessionKeys {
runtime::SessionKeys { aura: keys }
}

pub fn development_config() -> ChainSpec {
// Give your base currency a unit name and decimal places
let mut properties = sc_chain_spec::Properties::new();
Expand All @@ -80,35 +42,7 @@ pub fn development_config() -> ChainSpec {
.with_name("Development")
.with_id("dev")
.with_chain_type(ChainType::Development)
.with_genesis_config_patch(testnet_genesis(
// initial collators.
vec![
(
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_collator_keys_from_seed("Alice"),
),
(
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_collator_keys_from_seed("Bob"),
),
],
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
1000.into(),
))
.with_genesis_config_preset_name("development")
.build()
}

Expand All @@ -131,72 +65,8 @@ pub fn local_testnet_config() -> ChainSpec {
.with_name("Local Testnet")
.with_id("local_testnet")
.with_chain_type(ChainType::Local)
.with_genesis_config_patch(testnet_genesis(
// initial collators.
vec![
(
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_collator_keys_from_seed("Alice"),
),
(
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_collator_keys_from_seed("Bob"),
),
],
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
1000.into(),
))
.with_genesis_config_preset_name("local_testnet")
.with_protocol_id("template-local")
.with_properties(properties)
.build()
}

fn testnet_genesis(
invulnerables: Vec<(AccountId, AuraId)>,
endowed_accounts: Vec<AccountId>,
root: AccountId,
id: ParaId,
) -> serde_json::Value {
serde_json::json!({
"balances": {
"balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::<Vec<_>>(),
},
"parachainInfo": {
"parachainId": id,
},
"collatorSelection": {
"invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::<Vec<_>>(),
"candidacyBond": EXISTENTIAL_DEPOSIT * 16,
},
"session": {
"keys": invulnerables
.into_iter()
.map(|(acc, aura)| {
(
acc.clone(), // account id
acc, // validator id
template_session_keys(aura), // session keys
)
})
.collect::<Vec<_>>(),
},
"polkadotXcm": {
"safeXcmVersion": Some(SAFE_XCM_VERSION),
},
"sudo": { "key": Some(root) }
})
}
2 changes: 2 additions & 0 deletions templates/parachain/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ scale-info = { version = "2.11.1", default-features = false, features = [
] }
smallvec = "1.11.0"
docify = "0.2.8"
serde_json = { workspace = true, default-features = false }

# Local
pallet-parachain-template = { path = "../pallets/template", default-features = false }
Expand Down Expand Up @@ -127,6 +128,7 @@ std = [
"polkadot-parachain-primitives/std",
"polkadot-runtime-common/std",
"scale-info/std",
"serde_json/std",
"sp-api/std",
"sp-block-builder/std",
"sp-consensus-aura/std",
Expand Down
11 changes: 8 additions & 3 deletions templates/parachain/runtime/src/apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use sp_runtime::{
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult,
};
use sp_std::prelude::Vec;
use sp_std::prelude::*;
use sp_version::RuntimeVersion;

// Local module imports
Expand Down Expand Up @@ -279,11 +279,16 @@ impl_runtime_apis! {
}

fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
get_preset::<RuntimeGenesisConfig>(id, |_| None)
get_preset::<RuntimeGenesisConfig>(id, crate::genesis_config_presets::get_preset)
}

fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
Default::default()
use sp_genesis_builder::PresetId;
use crate::genesis_config_presets as presets;
vec![
PresetId::from(presets::PRESET_DEVELOPMENT),
PresetId::from(presets::PRESET_LOCAL_TESTNET),
]
}
}
}
165 changes: 165 additions & 0 deletions templates/parachain/runtime/src/genesis_config_presets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use cumulus_primitives_core::ParaId;
use sp_std::prelude::*;

pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;

use crate::{AccountId, SessionKeys, Signature, EXISTENTIAL_DEPOSIT};
use serde_json::Value;
use sp_core::{sr25519, Pair, Public};
use sp_runtime::traits::{IdentifyAccount, Verify};
#[cfg(not(feature = "std"))]
use sp_std::alloc::format;

/// Preset configuration name for a local testnet environment.
pub const PRESET_LOCAL_TESTNET: &str = "local_testnet";

/// Preset configuration name for a development environment.
pub const PRESET_DEVELOPMENT: &str = "development";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One annoying little detail about this is that if you don't put chian_type: Dev in the chain-spec, PJS apps won't show the accounts to you. There is a CLI flag for that two, but seem annoying to repeat development many times.

./chain-spec-builder --chain-type development --preset development etc etc 

We either create a ticket in PJS apps to change this (prob better), or we think about how we can improve it on our side. Maybe a chain-spec-builder --dev that does both? combined with running the chain in --dev it sounds like a good dev ex to me.

# compile wasm
# ./chain-spec-builder --runtime wasm --dev > spec.json
# ./omni-node --chain spec.json --dev

cc @gupnik @shawntabrizi

Copy link
Copy Markdown
Contributor Author

@michalkucharczyk michalkucharczyk Jun 10, 2024

Choose a reason for hiding this comment

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

chain-spec-builder --dev

This would assume that presets' naming is somehow standardized. Every runtime shall provide something in order to make flags in some other tool work correctly. This is probably OK, question is we want to go in that direction.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To me it sounds reasonable that these are separate flags. You could have multiple runtime presets for development, all of them with chainType "Dev".

I was thinking how one could do the pjs route. I think currently pjs calls the system_chainType RPC which gets its data directly from the chain spec. If we don't want to use the chain-spec property for this, we would need another easy way to tell that we are a dev chain.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can keep separate flags, --dev can be convenient shortcut.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yeah, let's keep all things configurable in the super-user mode, but one --dev that makes some assumptions is a good nice to have.


type AccountPublic = <Signature as Verify>::Signer;

/// The default XCM version to set in genesis config.
const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION;

/// Helper function to generate a crypto pair from seed
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
TPublic::Pair::from_string(&format!("//{}", seed), None)
.expect("static values are valid; qed")
.public()
}

/// 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::<AuraId>(seed)
}

/// Helper function to generate an account ID from seed
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
where
AccountPublic: From<<TPublic::Pair as Pair>::Public>,
{
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}

/// Generate the session keys from individual elements.
///
/// The input must be a tuple of individual keys (a single arg for now since we have just one key).
pub fn template_session_keys(keys: AuraId) -> SessionKeys {
SessionKeys { aura: keys }
}

fn testnet_genesis(
invulnerables: Vec<(AccountId, AuraId)>,
endowed_accounts: Vec<AccountId>,
root: AccountId,
id: ParaId,
) -> Value {
serde_json::json!({
"balances": {
"balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::<Vec<_>>(),
},
"parachainInfo": {
"parachainId": id,
},
"collatorSelection": {
"invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::<Vec<_>>(),
"candidacyBond": EXISTENTIAL_DEPOSIT * 16,
},
"session": {
"keys": invulnerables
.into_iter()
.map(|(acc, aura)| {
(
acc.clone(), // account id
acc, // validator id
template_session_keys(aura), // session keys
)
})
.collect::<Vec<_>>(),
},
"polkadotXcm": {
"safeXcmVersion": Some(SAFE_XCM_VERSION),
},
"sudo": { "key": Some(root) }
})
}

fn local_testnet_genesis() -> Value {
testnet_genesis(
// initial collators.
vec![
(
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_collator_keys_from_seed("Alice"),
),
(
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_collator_keys_from_seed("Bob"),
),
],
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah i have seen this crap in a lot of places being copy pasted 🙈

Similarly, I have what I think is a better way in #5117:

  1. sp_keyring::Keyring::iter()
  2. re-export it from frame as well for better discover-ability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I like it. Not sure however, if Keyring actually shall go to frame?
Let's do this in the followup - there will following work for solochain and minimal.

Some common parts need to be extracted, probably the accounts in presets can be unified somehow.
(also see #4739 (comment) which needs to be addressed).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@iulianbarbu this is a pretty easy one for you to pick up :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@kianenigma I am a bit late to the party. Based on the context on this thread I get that we want to do something like sp_keyring::Keyring::Alice.to_account_id() for the specific line where the thread started, and in general, for creating vec's we want to create them based on sp_keyring::Keyring::iter().

I do not get the context around re-export it from 'frame' as well for better discover-ability. Maybe we can sync offline (to not pollute anymore this merged PR) and maybe open an issue (if there isn't one) which clarifies next steps.

Copy link
Copy Markdown
Contributor Author

@michalkucharczyk michalkucharczyk Sep 13, 2024

Choose a reason for hiding this comment

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

It is about exporting that from the frame main library:

pub use sp_keyring::AccountKeyring;

Which was initially proposed by @kianenigma in this (not merged) #5117 PR.

Copy link
Copy Markdown
Contributor Author

@michalkucharczyk michalkucharczyk Sep 13, 2024

Choose a reason for hiding this comment

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

re-export it from 'frame' as well for better discover-ability basically means that you don't need extra use / crate dep (sp_keyring) when defining presets, it is just imported by frame::runtime.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see now. Thanks!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am trying re-export sp-keyring from frame in #5899, @kianenigma are you doing the similar work?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No, I am not doing a similar thing.

get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
1000.into(),
)
}

fn development_config_genesis() -> Value {
testnet_genesis(
// initial collators.
vec![
(
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_collator_keys_from_seed("Alice"),
),
(
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_collator_keys_from_seed("Bob"),
),
],
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
1000.into(),
)
}

/// Provides the JSON representation of predefined genesis config for given `id`.
pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option<sp_std::vec::Vec<u8>> {
let patch = match id.try_into() {
Ok(PRESET_LOCAL_TESTNET) => local_testnet_genesis(),
Ok(PRESET_DEVELOPMENT) => development_config_genesis(),
_ => return None,
};
Some(
serde_json::to_string(&patch)
.expect("serialization to json is expected to work. qed.")
.into_bytes(),
)
}
1 change: 1 addition & 0 deletions templates/parachain/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

pub mod apis;
mod configs;
mod genesis_config_presets;
mod weights;

use smallvec::smallvec;
Expand Down