diff --git a/substrate/bin/node/cli/src/chain_spec.rs b/substrate/bin/node/cli/src/chain_spec.rs index c30710d236..96888bd4ce 100644 --- a/substrate/bin/node/cli/src/chain_spec.rs +++ b/substrate/bin/node/cli/src/chain_spec.rs @@ -23,9 +23,9 @@ use sp_core::{Pair, Public, crypto::UncheckedInto, sr25519}; use serde::{Serialize, Deserialize}; use node_runtime::{ AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, - DemocracyConfig,GrandpaConfig, ImOnlineConfig, SessionConfig, SessionKeys, StakerStatus, + DemocracyConfig, GrandpaConfig, ImOnlineConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, ElectionsConfig, IndicesConfig, SocietyConfig, SudoConfig, SystemConfig, - TechnicalCommitteeConfig, wasm_binary_unwrap, + TechnicalCommitteeConfig, wasm_binary_unwrap, MAX_NOMINATIONS, }; use node_runtime::Block; use node_runtime::constants::currency::*; @@ -146,12 +146,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { let endowed_accounts: Vec = vec![root_key.clone()]; - testnet_genesis( - initial_authorities, - root_key, - Some(endowed_accounts), - false, - ) + testnet_genesis(initial_authorities, vec![], root_key, Some(endowed_accounts), false) } /// Staging testnet config. @@ -214,6 +209,7 @@ pub fn testnet_genesis( ImOnlineId, AuthorityDiscoveryId, )>, + initial_nominators: Vec, root_key: AccountId, endowed_accounts: Option>, enable_println: bool, @@ -234,11 +230,31 @@ pub fn testnet_genesis( get_account_id_from_seed::("Ferdie//stash"), ] }); - initial_authorities.iter().for_each(|x| - if !endowed_accounts.contains(&x.0) { - endowed_accounts.push(x.0.clone()) + // endow all authorities and nominators. + initial_authorities.iter().map(|x| &x.0).chain(initial_nominators.iter()).for_each(|x| { + if !endowed_accounts.contains(&x) { + endowed_accounts.push(x.clone()) } - ); + }); + + // stakers: all validators and nominators. + let mut rng = rand::thread_rng(); + let stakers = initial_authorities + .iter() + .map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)) + .chain(initial_nominators.iter().map(|x| { + use rand::{seq::SliceRandom, Rng}; + let limit = (MAX_NOMINATIONS as usize).min(initial_authorities.len()); + let count = rng.gen::() % limit; + let nominations = initial_authorities + .as_slice() + .choose_multiple(&mut rng, count) + .into_iter() + .map(|choice| choice.0.clone()) + .collect::>(); + (x.clone(), x.clone(), STASH, StakerStatus::Nominator(nominations)) + })) + .collect::>(); let num_endowed_accounts = endowed_accounts.len(); @@ -271,11 +287,9 @@ pub fn testnet_genesis( pallet_staking: StakingConfig { validator_count: initial_authorities.len() as u32 * 2, minimum_validator_count: initial_authorities.len() as u32, - stakers: initial_authorities.iter().map(|x| { - (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator) - }).collect(), invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), slash_reward_fraction: Perbill::from_percent(10), + stakers, .. Default::default() }, pallet_democracy: DemocracyConfig::default(), @@ -335,6 +349,7 @@ fn development_config_genesis() -> GenesisConfig { vec![ authority_keys_from_seed("Alice"), ], + vec![], get_account_id_from_seed::("Alice"), None, true, @@ -362,6 +377,7 @@ fn local_testnet_genesis() -> GenesisConfig { authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob"), ], + vec![], get_account_id_from_seed::("Alice"), None, false, @@ -395,6 +411,7 @@ pub(crate) mod tests { vec![ authority_keys_from_seed("Alice"), ], + vec![], get_account_id_from_seed::("Alice"), None, false, diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 8f8f826488..2d763979c7 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -472,8 +472,7 @@ parameter_types! { } impl pallet_staking::Config for Runtime { - const MAX_NOMINATIONS: u32 = - ::LIMIT as u32; + const MAX_NOMINATIONS: u32 = MAX_NOMINATIONS; type Currency = Balances; type UnixTime = Timestamp; type CurrencyToVote = U128CurrencyToVote; @@ -527,6 +526,9 @@ sp_npos_elections::generate_solution_type!( >(16) ); +pub const MAX_NOMINATIONS: u32 = + ::LIMIT as u32; + impl pallet_election_provider_multi_phase::Config for Runtime { type Event = Event; type Currency = Balances; diff --git a/substrate/bin/utils/chain-spec-builder/src/main.rs b/substrate/bin/utils/chain-spec-builder/src/main.rs index f3336b1d53..2aaef7c96d 100644 --- a/substrate/bin/utils/chain-spec-builder/src/main.rs +++ b/substrate/bin/utils/chain-spec-builder/src/main.rs @@ -41,6 +41,10 @@ enum ChainSpecBuilder { /// Authority key seed. #[structopt(long, short, required = true)] authority_seeds: Vec, + /// Active nominators (SS58 format), each backing a random subset of the aforementioned + /// authorities. + #[structopt(long, short, default_value = "0")] + nominator_accounts: Vec, /// Endowed account address (SS58 format). #[structopt(long, short)] endowed_accounts: Vec, @@ -57,6 +61,11 @@ enum ChainSpecBuilder { /// The number of authorities. #[structopt(long, short)] authorities: usize, + /// The number of nominators backing the aforementioned authorities. + /// + /// Will nominate a random subset of `authorities`. + #[structopt(long, short, default_value = "0")] + nominators: usize, /// The number of endowed accounts. #[structopt(long, short, default_value = "0")] endowed: usize, @@ -87,6 +96,7 @@ impl ChainSpecBuilder { fn genesis_constructor( authority_seeds: &[String], + nominator_accounts: &[AccountId], endowed_accounts: &[AccountId], sudo_account: &AccountId, ) -> chain_spec::GenesisConfig { @@ -100,6 +110,7 @@ fn genesis_constructor( chain_spec::testnet_genesis( authorities, + nominator_accounts.to_vec(), sudo_account.clone(), Some(endowed_accounts.to_vec()), enable_println, @@ -108,26 +119,28 @@ fn genesis_constructor( fn generate_chain_spec( authority_seeds: Vec, + nominator_accounts: Vec, endowed_accounts: Vec, sudo_account: String, ) -> Result { - let parse_account = |address: &String| { - AccountId::from_string(address) + let parse_account = |address: String| { + AccountId::from_string(&address) .map_err(|err| format!("Failed to parse account address: {:?}", err)) }; - let endowed_accounts = endowed_accounts - .iter() - .map(parse_account) - .collect::, String>>()?; + let nominator_accounts = + nominator_accounts.into_iter().map(parse_account).collect::, String>>()?; - let sudo_account = parse_account(&sudo_account)?; + let endowed_accounts = + endowed_accounts.into_iter().map(parse_account).collect::, String>>()?; + + let sudo_account = parse_account(sudo_account)?; let chain_spec = chain_spec::ChainSpec::from_genesis( "Custom", "custom", sc_chain_spec::ChainType::Live, - move || genesis_constructor(&authority_seeds, &endowed_accounts, &sudo_account), + move || genesis_constructor(&authority_seeds, &nominator_accounts, &endowed_accounts, &sudo_account), vec![], None, None, @@ -186,6 +199,7 @@ fn generate_authority_keys_and_store( fn print_seeds( authority_seeds: &[String], + nominator_seeds: &[String], endowed_seeds: &[String], sudo_seed: &str, ) { @@ -201,6 +215,12 @@ fn print_seeds( ); } + println!("{}", header.paint("Nominator seeds")); + + for (n, seed) in nominator_seeds.iter().enumerate() { + println!("{} //{}", entry.paint(format!("nom-{}:", n)), seed); + } + println!(); if !endowed_seeds.is_empty() { @@ -220,34 +240,27 @@ fn print_seeds( } fn main() -> Result<(), String> { - #[cfg(build_type="debug")] + #[cfg(build_type = "debug")] println!( - "The chain spec builder builds a chain specification that includes a Substrate runtime compiled as WASM. To \ - ensure proper functioning of the included runtime compile (or run) the chain spec builder binary in \ - `--release` mode.\n", + "The chain spec builder builds a chain specification that includes a Substrate runtime \ + compiled as WASM. To ensure proper functioning of the included runtime compile (or run) \ + the chain spec builder binary in `--release` mode.\n", ); let builder = ChainSpecBuilder::from_args(); let chain_spec_path = builder.chain_spec_path().to_path_buf(); - let (authority_seeds, endowed_accounts, sudo_account) = match builder { - ChainSpecBuilder::Generate { authorities, endowed, keystore_path, .. } => { + let (authority_seeds, nominator_accounts, endowed_accounts, sudo_account) = match builder { + ChainSpecBuilder::Generate { authorities, nominators, endowed, keystore_path, .. } => { let authorities = authorities.max(1); - let rand_str = || -> String { - OsRng.sample_iter(&Alphanumeric) - .take(32) - .collect() - }; + let rand_str = || -> String { OsRng.sample_iter(&Alphanumeric).take(32).collect() }; let authority_seeds = (0..authorities).map(|_| rand_str()).collect::>(); + let nominator_seeds = (0..nominators).map(|_| rand_str()).collect::>(); let endowed_seeds = (0..endowed).map(|_| rand_str()).collect::>(); let sudo_seed = rand_str(); - print_seeds( - &authority_seeds, - &endowed_seeds, - &sudo_seed, - ); + print_seeds(&authority_seeds, &nominator_seeds, &endowed_seeds, &sudo_seed); if let Some(keystore_path) = keystore_path { generate_authority_keys_and_store( @@ -256,23 +269,37 @@ fn main() -> Result<(), String> { )?; } - let endowed_accounts = endowed_seeds.iter().map(|seed| { - chain_spec::get_account_id_from_seed::(seed) - .to_ss58check() - }).collect(); + let nominator_accounts = nominator_seeds + .into_iter() + .map(|seed| { + chain_spec::get_account_id_from_seed::(&seed).to_ss58check() + }) + .collect(); - let sudo_account = chain_spec::get_account_id_from_seed::(&sudo_seed) - .to_ss58check(); + let endowed_accounts = endowed_seeds + .into_iter() + .map(|seed| { + chain_spec::get_account_id_from_seed::(&seed).to_ss58check() + }) + .collect(); - (authority_seeds, endowed_accounts, sudo_account) - }, - ChainSpecBuilder::New { authority_seeds, endowed_accounts, sudo_account, .. } => { - (authority_seeds, endowed_accounts, sudo_account) - }, + let sudo_account = + chain_spec::get_account_id_from_seed::(&sudo_seed).to_ss58check(); + + (authority_seeds, nominator_accounts, endowed_accounts, sudo_account) + } + ChainSpecBuilder::New { + authority_seeds, + nominator_accounts, + endowed_accounts, + sudo_account, + .. + } => (authority_seeds, nominator_accounts, endowed_accounts, sudo_account), }; let json = generate_chain_spec( authority_seeds, + nominator_accounts, endowed_accounts, sudo_account, )?;