1use polkadot_sdk::*;
22
23use crate::chain_spec::sc_service::Properties;
24use kitchensink_runtime::{
25 genesis_config_presets::{Staker, ENDOWMENT, STASH},
26 wasm_binary_unwrap, Block, MaxNominations, StakerStatus,
27};
28use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
29use pallet_revive::is_eth_derived;
30use sc_chain_spec::ChainSpecExtension;
31use sc_service::ChainType;
32use sc_telemetry::TelemetryEndpoints;
33use serde::{Deserialize, Serialize};
34use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
35use sp_consensus_babe::AuthorityId as BabeId;
36use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId;
37use sp_consensus_grandpa::AuthorityId as GrandpaId;
38use sp_core::crypto::UncheckedInto;
39use sp_mixnet::types::AuthorityId as MixnetId;
40
41pub use kitchensink_runtime::RuntimeGenesisConfig;
42pub use node_primitives::{AccountId, Balance, Signature};
43
44const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
45
46#[derive(Default, Clone, Serialize, Deserialize, ChainSpecExtension)]
51#[serde(rename_all = "camelCase")]
52pub struct Extensions {
53 pub fork_blocks: sc_client_api::ForkBlocks<Block>,
55 pub bad_blocks: sc_client_api::BadBlocks<Block>,
57 pub light_sync_state: sc_sync_state_rpc::LightSyncStateExtension,
59}
60
61pub type ChainSpec = sc_service::GenericChainSpec<Extensions>;
63pub fn flaming_fir_config() -> Result<ChainSpec, String> {
65 ChainSpec::from_json_bytes(&include_bytes!("../res/flaming-fir.json")[..])
66}
67
68fn configure_accounts_for_staging_testnet() -> (
69 Vec<(
70 AccountId,
71 AccountId,
72 GrandpaId,
73 BabeId,
74 ImOnlineId,
75 AuthorityDiscoveryId,
76 MixnetId,
77 BeefyId,
78 )>,
79 AccountId,
80 Vec<AccountId>,
81) {
82 #[rustfmt::skip]
83 let initial_authorities: Vec<(
96 AccountId,
97 AccountId,
98 GrandpaId,
99 BabeId,
100 ImOnlineId,
101 AuthorityDiscoveryId,
102 MixnetId,
103 BeefyId,
104 )> = vec![
105 (
106 array_bytes::hex_n_into_unchecked("9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"),
108 array_bytes::hex_n_into_unchecked("781ead1e2fa9ccb74b44c19d29cb2a7a4b5be3972927ae98cd3877523976a276"),
110 array_bytes::hex2array_unchecked("9becad03e6dcac03cee07edebca5475314861492cdfc96a2144a67bbe9699332")
112 .unchecked_into(),
113 array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
115 .unchecked_into(),
116 array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
118 .unchecked_into(),
119 array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
121 .unchecked_into(),
122 array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
124 .unchecked_into(),
125 array_bytes::hex2array_unchecked("035560fafa241739869360aa4b32bc98953172ceb41a19c6cc1a27962fb3d1ecec")
127 .unchecked_into(),
128 ),
129 (
130 array_bytes::hex_n_into_unchecked("68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"),
132 array_bytes::hex_n_into_unchecked("c8dc79e36b29395413399edaec3e20fcca7205fb19776ed8ddb25d6f427ec40e"),
134 array_bytes::hex2array_unchecked("7932cff431e748892fa48e10c63c17d30f80ca42e4de3921e641249cd7fa3c2f")
136 .unchecked_into(),
137 array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
139 .unchecked_into(),
140 array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
142 .unchecked_into(),
143 array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
145 .unchecked_into(),
146 array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
148 .unchecked_into(),
149 array_bytes::hex2array_unchecked("02da1ab255ed888ee3e19b73d335fc13160b3eb10456c2d17c6a8ea7de403d2445")
151 .unchecked_into(),
152 ),
153 (
154 array_bytes::hex_n_into_unchecked("547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"),
156 array_bytes::hex_n_into_unchecked("9e42241d7cd91d001773b0b616d523dd80e13c6c2cab860b1234ef1b9ffc1526"),
158 array_bytes::hex2array_unchecked("5633b70b80a6c8bb16270f82cca6d56b27ed7b76c8fd5af2986a25a4788ce440")
160 .unchecked_into(),
161 array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
163 .unchecked_into(),
164 array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
166 .unchecked_into(),
167 array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
169 .unchecked_into(),
170 array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
172 .unchecked_into(),
173 array_bytes::hex2array_unchecked("036a818b3f59579c5fbbe4fede64f49dbf090ba883eb2a175d5ca90e5adb5f0b3e")
175 .unchecked_into(),
176 ),
177 (
178 array_bytes::hex_n_into_unchecked("f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"),
180 array_bytes::hex_n_into_unchecked("66bc1e5d275da50b72b15de072a2468a5ad414919ca9054d2695767cf650012f"),
182 array_bytes::hex2array_unchecked("3919132b851ef0fd2dae42a7e734fe547af5a6b809006100f48944d7fae8e8ef")
184 .unchecked_into(),
185 array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
187 .unchecked_into(),
188 array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
190 .unchecked_into(),
191 array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
193 .unchecked_into(),
194 array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
196 .unchecked_into(),
197 array_bytes::hex2array_unchecked("020ce02b963548f9f8ade8765f7a4a06638c17819c78422a1cc35b647873583eef")
199 .unchecked_into(),
200 ),
201 ];
202
203 let root_key: AccountId = array_bytes::hex_n_into_unchecked(
205 "9ee5e5bdc0ec239eb164f865ecc345ce4c88e76ee002e0f7e318097347471809",
207 );
208
209 let endowed_accounts: Vec<AccountId> = vec![root_key.clone()];
210 (initial_authorities, root_key, endowed_accounts)
211}
212
213fn staging_testnet_genesis_patch() -> serde_json::Value {
214 let (initial_authorities, root_key, endowed_accounts) =
215 configure_accounts_for_staging_testnet();
216 testnet_genesis_patch(initial_authorities, vec![], root_key, endowed_accounts)
217}
218
219pub fn staging_testnet_config() -> ChainSpec {
221 ChainSpec::builder(wasm_binary_unwrap(), Default::default())
222 .with_name("Staging Testnet")
223 .with_id("staging_testnet")
224 .with_chain_type(ChainType::Live)
225 .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET)
226 .with_genesis_config_patch(staging_testnet_genesis_patch())
227 .with_telemetry_endpoints(
228 TelemetryEndpoints::new(vec![(STAGING_TELEMETRY_URL.to_string(), 0)])
229 .expect("Staging telemetry url is valid; qed"),
230 )
231 .build()
232}
233
234fn configure_accounts(
239 initial_authorities: Vec<(
240 AccountId,
241 AccountId,
242 GrandpaId,
243 BabeId,
244 ImOnlineId,
245 AuthorityDiscoveryId,
246 MixnetId,
247 BeefyId,
248 )>,
249 initial_nominators: Vec<AccountId>,
250 endowed_accounts: Vec<AccountId>,
251 stash: Balance,
252) -> (
253 Vec<(
254 AccountId,
255 AccountId,
256 GrandpaId,
257 BabeId,
258 ImOnlineId,
259 AuthorityDiscoveryId,
260 MixnetId,
261 BeefyId,
262 )>,
263 Vec<AccountId>,
264 Vec<Staker>,
265) {
266 let mut endowed_accounts = endowed_accounts;
267 initial_authorities
269 .iter()
270 .map(|x| &x.0)
271 .chain(initial_nominators.iter())
272 .for_each(|x| {
273 if !endowed_accounts.contains(x) {
274 endowed_accounts.push(x.clone())
275 }
276 });
277
278 let mut rng = rand::thread_rng();
280 let stakers = initial_authorities
281 .iter()
282 .map(|x| (x.0.clone(), x.0.clone(), stash, StakerStatus::Validator))
283 .chain(initial_nominators.iter().map(|x| {
284 use rand::{seq::SliceRandom, Rng};
285 let limit = (MaxNominations::get() as usize).min(initial_authorities.len());
286 let count = rng.gen::<usize>() % limit;
287 let nominations = initial_authorities
288 .as_slice()
289 .choose_multiple(&mut rng, count)
290 .into_iter()
291 .map(|choice| choice.0.clone())
292 .collect::<Vec<_>>();
293 (x.clone(), x.clone(), stash, StakerStatus::Nominator(nominations))
294 }))
295 .collect::<Vec<_>>();
296
297 (initial_authorities, endowed_accounts, stakers)
298}
299
300pub fn testnet_genesis_patch(
302 initial_authorities: Vec<(
303 AccountId,
304 AccountId,
305 GrandpaId,
306 BabeId,
307 ImOnlineId,
308 AuthorityDiscoveryId,
309 MixnetId,
310 BeefyId,
311 )>,
312 initial_nominators: Vec<AccountId>,
313 root_key: AccountId,
314 endowed_accounts: Vec<AccountId>,
315) -> serde_json::Value {
316 let (initial_authorities, endowed_accounts, stakers) =
317 configure_accounts(initial_authorities, initial_nominators, endowed_accounts, STASH);
318
319 let validator_count = initial_authorities.len();
320 let minimum_validator_count = validator_count;
321
322 let collective = collective(&endowed_accounts);
323
324 serde_json::json!({
325 "balances": {
326 "balances": endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect::<Vec<_>>()
327 },
328 "session": {
329 "keys": initial_authorities
330 .iter()
331 .map(|x| {
332 (
333 x.0.clone(),
334 x.0.clone(),
336 session_keys_json(
337 x.2.clone(),
338 x.3.clone(),
339 x.4.clone(),
340 x.5.clone(),
341 x.6.clone(),
342 x.7.clone(),
343 )
344 )
345 })
346 .collect::<Vec<_>>()
347 },
348 "elections": {
349 "members": collective.iter().cloned().map(|member| (member, STASH)).collect::<Vec<_>>(),
350 },
351 "technicalCommittee": {
352 "members": collective,
353 },
354 "staking": {
355 "validatorCount": validator_count,
356 "minimumValidatorCount": minimum_validator_count,
357 "invulnerables": initial_authorities
358 .iter()
359 .map(|x| x.0.clone())
360 .collect::<Vec<_>>(),
361 "stakers": stakers,
362 },
363 "sudo": {
364 "key": root_key,
365 },
366 "revive": {
367 "mappedAccounts": endowed_accounts.iter().filter(|x| ! is_eth_derived(x)).cloned().collect::<Vec<_>>()
368 }
369 })
370}
371
372fn session_keys_json(
374 grandpa: GrandpaId,
375 babe: BabeId,
376 im_online: ImOnlineId,
377 authority_discovery: AuthorityDiscoveryId,
378 mixnet: MixnetId,
379 beefy: BeefyId,
380) -> serde_json::Value {
381 serde_json::json!({
382 "authority_discovery": authority_discovery,
383 "babe": babe,
384 "beefy": beefy,
385 "grandpa": grandpa,
386 "im_online": im_online,
387 "mixnet": mixnet
388 })
389}
390
391fn collective(endowed: &[AccountId]) -> Vec<AccountId> {
393 const MAX_COLLECTIVE_SIZE: usize = 50;
394 let endowed_accounts_count = endowed.len();
395 endowed
396 .iter()
397 .take((endowed_accounts_count.div_ceil(2)).min(MAX_COLLECTIVE_SIZE))
398 .cloned()
399 .collect()
400}
401
402fn props() -> Properties {
403 let mut properties = Properties::new();
404 properties.insert("tokenDecimals".to_string(), 12.into());
405 properties
406}
407
408pub fn development_config() -> ChainSpec {
410 ChainSpec::builder(wasm_binary_unwrap(), Default::default())
411 .with_name("Development")
412 .with_id("dev")
413 .with_chain_type(ChainType::Development)
414 .with_properties(props())
415 .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET)
416 .build()
417}
418
419pub fn local_testnet_config() -> ChainSpec {
421 ChainSpec::builder(wasm_binary_unwrap(), Default::default())
422 .with_name("Local Testnet")
423 .with_id("local_testnet")
424 .with_chain_type(ChainType::Local)
425 .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET)
426 .build()
427}
428
429#[cfg(test)]
430pub(crate) mod tests {
431 use super::*;
432 use crate::service::{new_full_base, NewFullBase};
433 use kitchensink_runtime::genesis_config_presets::well_known_including_eth_accounts;
434 use sc_service_test;
435 use sp_runtime::{AccountId32, BuildStorage};
436
437 pub fn integration_test_config_with_single_authority() -> ChainSpec {
439 ChainSpec::builder(wasm_binary_unwrap(), Default::default())
440 .with_name("Integration Test")
441 .with_id("test")
442 .with_chain_type(ChainType::Development)
443 .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET)
444 .build()
445 }
446
447 pub fn integration_test_config_with_two_authorities() -> ChainSpec {
449 ChainSpec::builder(wasm_binary_unwrap(), Default::default())
450 .with_name("Integration Test")
451 .with_id("test")
452 .with_chain_type(ChainType::Local)
453 .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET)
454 .build()
455 }
456
457 fn eth_account(from: subxt_signer::eth::Keypair) -> AccountId32 {
458 let mut account_id = AccountId32::new([0xEE; 32]);
459 <AccountId32 as AsMut<[u8; 32]>>::as_mut(&mut account_id)[..20]
460 .copy_from_slice(&from.public_key().to_account_id().as_ref());
461 account_id
462 }
463
464 #[test]
465 #[ignore]
466 fn test_connectivity() {
467 sp_tracing::try_init_simple();
468
469 sc_service_test::connectivity(integration_test_config_with_two_authorities(), |config| {
470 let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } =
471 new_full_base::<sc_network::NetworkWorker<_, _>>(config, None, false, |_, _| ())?;
472 Ok(sc_service_test::TestNetComponents::new(
473 task_manager,
474 client,
475 network,
476 sync,
477 transaction_pool,
478 ))
479 });
480 }
481
482 #[test]
483 fn test_create_development_chain_spec() {
484 development_config().build_storage().unwrap();
485 }
486
487 #[test]
488 fn test_create_local_testnet_chain_spec() {
489 local_testnet_config().build_storage().unwrap();
490 }
491
492 #[test]
493 fn test_staging_test_net_chain_spec() {
494 staging_testnet_config().build_storage().unwrap();
495 }
496
497 #[test]
498 fn ensure_eth_accounts_are_in_endowed() {
499 let alith = eth_account(subxt_signer::eth::dev::alith());
500 let baltathar = eth_account(subxt_signer::eth::dev::baltathar());
501
502 let endowed = well_known_including_eth_accounts();
503
504 assert!(endowed.contains(&alith), "Alith must be in endowed for integration tests");
505 assert!(endowed.contains(&baltathar), "Baltathar must be in endowed for integration tests");
506 }
507}